比赛题目训练系列18 (NWERC 2020)

比赛题目训练系列18 (NWERC 2020)

Atomic Energy

Atomic Energy

  • 一个原子核在裂变
    比赛题目训练系列18 (NWERC 2020)_第1张图片
  • 让原子核放出的能量最少. 现在给 t ( t ≤ 1 0 5 ) t(t \le 10^5) t(t105) 个测试, 每次询问的原子核的大小是 k ( k ≤ 1 0 9 ) k(k \le 10^9) k(k109).
  • 这个题和之前小米那个不一样。小米的是01背包。
  • 一开始优先选择性价比最优的,到达一定范围的时候,就开始跑暴力。因此暴力可以预处理,至于暴力的范围,可以猜一猜,至少这个题预处理出 n 2 n^2 n2 即可.
  • 这样做的原理是,前面跑暴力之后,后面会形成一个等差序列,然后找到最优的起点即可.
#include
using namespace std;
const int N = 21000;
typedef long long ll;
ll f[N], a[N];
int n, q;
int main()
{
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for(int i = n + 1; i <= n * n; i++) f[i] = 1e18;
    for(int i = 1; i <= n; i++) f[i] = a[i];
    for(int i = n + 1; i <= n * n; i++){
        for(int j = 1; j < i; j++){
            f[i] = min(f[i], f[j] + f[i - j]);
        }
    }
    ll mina = a[1], minv = 1;
    for(ll i = 1; i <= n; i++){
        if(mina * i > minv * a[i]){
            mina = a[i];
            minv = i;
        }
    }
    while(q--){
        ll k;
        scanf("%lld", &k);
        if(k <= n * n){
            printf("%lld\n", f[k]);
        }
        else{
            ll cnt = (k - n * n) / minv - 1;

            while(k - cnt * minv > n * n) cnt++;
            printf("%lld\n", cnt * mina + f[k - cnt * minv]);
        }
    }
    return 0;
}

I. Island Tour

  • 题意:有一个环形道路有 n 个点( n ≤ 400 n \le 400 n400),给出从第 i i i 个点到第 i + 1 i+1 i+1 个点的观光时间. 然后给出这三个人在每个观光点需要花费多少时间。现在让找到三个点作为这三个人的起始观光点,保证这三个人不会碰面(一个人从一个观光点的离开的同时另一个人到这个观光点不算碰面),一个人旅游完最后一个观光点后会立刻离开。
  • 一个很简单的思路是,分开预处理,每次处理两个人, O ( n 2 ) O(n^2) O(n2) 枚举他们分别的起点,然后 O ( n ) O(n) O(n) 的时间判断他们是否会相遇。然后再枚举三个人的起点(看看 d i s a b ( i , j ) , d i s a c ( i , k ) , d i s b c ( j , k ) disab(i,j),disac(i, k), disbc(j, k) disab(i,j),disac(i,k),disbc(j,k) 是否都满足).
  • 预处理的地方,对于每一个点,计算两个人的到达时间和离开时间,看看这两个线段是否相交即可。但是注意一个线段右端点和另一个线段的左端点相交算合法的.
#include
using namespace std;
const int N = 410;
int n, a[N], b[N], c[N], d[N];
int disab[N][N], disac[N][N], disbc[N][N];
bool check(int a[], int b[], int s1, int s2)
{
    static int st1[N], ed1[N], st2[N], ed2[N];
    memset(st1, 0, sizeof st1);
    memset(ed1, 0, sizeof ed1);
    memset(st2, 0, sizeof st2);
    memset(ed2, 0, sizeof ed2);
    st1[s1] = st2[s2] = 0, ed1[s1] = a[s1], ed2[s2] = b[s2];
    for(int i = 1; i < n; i++){
        st1[(s1 + i) % n] = ed1[(s1 + i - 1) % n] + d[(s1 + i - 1) % n];
        ed1[(s1 + i) % n] = st1[(s1 + i) % n] + a[(s1 + i) % n];
        st2[(s2 + i) % n] = ed2[(s2 + i - 1) % n] + d[(s2 + i - 1) % n];
        ed2[(s2 + i) % n] = st2[(s2 + i) % n] + b[(s2 + i) % n];
    }
    for(int i = 0; i < n; i++){
        if(st1[i] <= st2[i] && ed1[i] > st2[i]) return false;
        if(st1[i] >= st2[i] && st1[i] < ed2[i]) return false;
    }
    return true;
}
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%d", &d[i]);
    for(int i = 0; i < n; i++) scanf("%d", &a[i]);
    for(int i = 0; i < n; i++) scanf("%d", &b[i]);
    for(int i = 0; i < n; i++) scanf("%d", &c[i]);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            if(i == j) continue;
            disab[i][j] = check(a, b, i, j);
            disac[i][j] = check(a, c, i, j);
            disbc[i][j] = check(b, c, i, j);
        }
    }
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            for(int k = 0; k < n; k++){
                if(disab[i][j] && disac[i][k] && disbc[j][k]){
                    printf("%d %d %d\n", i + 1, j + 1, k + 1);
                    return 0;
                }
            }
        }
    }
    printf("impossible\n");
    return 0;
}

你可能感兴趣的:(ACM题目整理,算法)