CF G. Running Competition (NTT, 思维)

题目:传送门

题意

在直角坐标系中,有 n + 1 条线段,第一条线段连接着 (0, 0) , (0, y),最后一条线段连接着 (x, 0) , (x,y),第 i 条线段,连接着 (ai, 0) , (ai, y), 0 = a0 < a1 < a2 < .... < an = n。你能从任何点开始绕着线段走,并最终回到出发点,且你不能走已经走过的点,你走过的线段的总长度就是你此次行动得到的 len.

 

 如图,红色方框的长度即为此次行动得到的 len, 即 len = 24。

现在有 q 次询问,每次询问输入一个数 Li, 问你是否存在一种行动,使得行动得到的 len,满足 Li % len == 0,若存在输出最大的 len,不存在输出 -1.

 

 思路

观察发现,每次行动必定是一个矩形,且其中两条边的长度等于 y,另外两条边的长度等于 ai - aj (0 <= j < i <= n).

我们令 b[i] = x - a[i],构造两个多项式 A, B,A[i] 的第 a[i] 项的系数为 1,B[i] 的第 b[i] 项的系数为 1.

令多项式 C = A * B,多项式相乘可以用 NTT 做,复杂度 o(n*logn)

若 C[k] 的系数不为 0,则表示存在数组 a 的一个数 a[i] 和 数组 b 的一个数  b[j]  相加等于 k.

又 a[i] + b[j] = a[i] - a[j] + x,那我们可以通过判断 C[ tmp + x ] 是否不为 0,从而来判断是否存在数组 a 的两个数, a[i],a[j],使得 a[i] - a[j] = tmp

由于 a[i] - a[j] 的取值范围为 [1, x], 所以我们只需要枚举 k = [1,x] 判断 C[ k + x ] 是否等于 0,若不等于 0,则用 2 * (k + y) 去更新答案数组,复杂度 o(nlogn)

 

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 1e6 + 5;

const LL G = 3;
const LL mod = 998244353;
int len, r[N];
LL g[N], f[N], w[N], x[N], y[N];
LL power(LL x,LL y) {
    LL ans = 1;
    while(y) {
        if( y & 1 ) ans = ans * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return ans;
}
void ntt(LL *a, LL f) {
    for (LL i = 0; i < len; i++) {
        if (i < r[i]) swap(a[i], a[r[i]]);
    }
    w[0] = 1;
    for (LL i = 2; i <= len; i *= 2) {
        LL wn;
        if (f == 1) wn = power(G, (LL)(mod - 1) / i);
        else wn = power(G, (LL)(mod - 1) - (mod - 1) / i);
        for (LL j = i / 2; j >= 0; j -= 2) w[j] = w[j / 2];
        for (LL j = 1; j < i / 2; j += 2) w[j] = (w[j - 1] * wn) % mod;
        for (LL j = 0; j < len; j += i) {
            for (LL k = 0 ; k < i / 2; k++) {
                LL u = a[j + k], v = (a[j + k + i / 2] * w[k]) % mod;
                a[j + k] = (u + v) % mod;
                a[j + k + i / 2] = (u - v + mod) % mod;
            }
        }
    }
    if (f == -1) {
        LL inv = power(len, mod - 2);
        for (LL i = 0; i < len; i++) a[i] = (a[i] * inv) % mod;
    }
}
void NTT(LL *a, LL *b, LL *c, LL n, LL m) {
    len = 1;
    while (len <= (n + m)) len *= 2;
    LL k = trunc(log(len + 0.5) / log(2));
    for (LL i = 0; i < len; i++) {
        r[i] = (r[i >> 1] >> 1) | ((i & 1) << (k - 1));
    }
    for (LL i = 0; i < len; i++) {
        if (i < n) x[i] = a[i]; else x[i] = 0;
        if (i < m) y[i] = b[i]; else y[i] = 0;
    }
    ntt(x, 1); ntt(y, 1);
    for (LL i = 0; i < len; i++) c[i] = x[i] * y[i] % mod;
    ntt(c, -1);
}

LL n, X, Y, a[N];

LL A[N], B[N], ans[N];

void solve() {

    mem(ans, -1);

    scanf("%lld %lld %lld", &n, &X, &Y);

    rep(i, 0, n) {

        scanf("%lld", &a[i]);

        A[a[i]] = 1;

        B[X - a[i]] = 1;

    }

    NTT(A, B, A, X + 1, X + 1);

    rep(i, 1, X) {

        if(A[X + i]) {

            for(int j = 2 * (i + Y); j < N; j += 2 * (i + Y)) {

                ans[j] = 2 * (i + Y);

            }

        }

    }

    int q; scanf("%d", &q);

    while(q--) {

        int p; scanf("%d", &p);

        printf("%lld\n", ans[p]);

    }

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}

 

posted on 2020-09-03 15:28  Willems  阅读(192)  评论(0编辑  收藏  举报

导航