[Codeforces 1251F]レッド、ホワイトフェンス

説明

試験のリンク

あなた\(\ N-)白い木のブロックは、\(K \) 赤木材をブロックそれぞれの長さを持っている\(H_I \) それはあなたが満たすためにフェンシングのこれらの厚板の部分を使用することができます:

  1. 唯一の赤い木、およびすべてのホワイトボードの長さは、赤い木の長さよりも厳密に小さいです。
  2. ホワイト木製ボード赤長さが増加し、厳密に単調左。
  3. 減少厳密に単調な白い木製の厚板の赤右の長さ

今、与えられた\(q個\)グループは尋ねた円周尋ねた\(X_Iを\)どのように多くのフェンス。

\(1個の\当量のN、H_I、Q \当量3 \ CDOT ^ 5,4 10 \当量X_I \当量12 \ CDOT 10 ^ 5,1 \当量のK \当量5 \)

解決

私たちは、赤いボードの長さを選択した場合、\(Lの\)によると、このフェンス\(M \)ボードを構成するブロック、明らかに周囲の\(2 \タイムズ(L + m)を\)

問題は、それまでに求めに変換\(M-1 \)ブロック長\(<L \)ホワイトボードの数は実施形態2つの長さに厳密に単調増加するシーケンスに形成されます。

木材の長さが離散的であるので、我々はそれぞれの長さのreleaseメソッドの厚板を考慮することができます。

我々は、すべての長さに置く(<L \)\ホワイトボードを削除します。木材は、特定の長さである場合。だから、明らかに、このボードは(つまり、配列のいずれか一つである)赤い木の左または右に配置することができます。

二つ以上の特定の長さのために、私たちは、左側に彼を置く右または両側に置くことができます。そして、我々は唯一まで使用されます(2 \)\過剰を除去することができるので、木材などのブロックを。

最初のケースであるという仮定の下で基板の数(木材片の長さのみ)\(SA \)を明らかにこれと\(SA \) 2つの配列の全長を構成する板\(Iは\)プログラム番号の\(= a_iを{SA \ I} \ 2倍^ I \を選択)

第2のケースでボードの数されていると(SB \)\を(過剰木を除去するため)。これにより\(SB \) 2つの配列の全長を構成する板\(Iは\)番号スキーム\(SB B_iは= {\ I} \選択)

どちらの場合も、長さの合計と呼ば\(私は\)番号スキーム\(C_I \) その後、我々は必要では見つけることは簡単です。

\ [C_ {M-1} = \ sum_ {i = 0} ^ {M-1} a_ib_ {M-1-I} \]

これは、畳み込み型であり、我々はセットアップ

\ [\開始{整列} A(X)&= \ sum_i a_iをX ^ iは\\ B(X)&= \ sum_i b_i X ^ iは\\ C(X)&= A(X)\ otimes B(X )\\&= \ sum_i C_I X ^ iは端{整列を} \ \]

その後、我々が使用できる\を(\テキスト{NTT} \ ) 場合、異なるホワイトボードの数から選択された番組の数に対応した木材から選択された赤取得。

したがって、我々はやる、赤い木のそれぞれを列挙することができます\(\ {テキストNTT} \ ) 、答えるために蓄積し(O(1)\)\質問に答えます。

全体的に複雑\(O(K \ n倍\ログのn-Qの+)\)

コード

#include <bits/stdc++.h>
using namespace std;
const int N = 12e5+5, yzh = 998244353;

int n, k, q, cnt[N], x, ans[N], fac[N], ifac[N];
int A[N], B[N], a, b, L, R[N];

int quick_pow(int a, int b) {
    int ans = 1;
    while (b) {
        if (b&1) ans = 1ll*ans*a%yzh;
        b >>= 1, a = 1ll*a*a%yzh;
    }
    return ans;
}
int C(int n, int m) {return 1ll*fac[n]*ifac[m]%yzh*ifac[n-m]%yzh; }
void NTT(int *A, int o) {
    for (int i = 0; i < n; i++) if (i < R[i]) swap(A[i], A[R[i]]);
    for (int i = 1; i < n; i <<= 1) {
        int gn = quick_pow(3, (yzh-1)/(i<<1)), x, y;
        if (o == -1) gn = quick_pow(gn, yzh-2);
        for (int j = 0; j < n; j += (i<<1)) {
            int g = 1;
            for (int k = 0; k < i; k++, g = 1ll*g*gn%yzh) {
                x = A[j+k], y = 1ll*g*A[j+k+i]%yzh;
                A[j+k] = (x+y)%yzh;
                A[j+k+i] = (x-y)%yzh;
            }
        }
    }
}
int main() {
    scanf("%d%d", &n, &k);
    fac[0] = ifac[0] = ifac[1] = 1;
    for (int i = 2; i <= n; i++) ifac[i] = -1ll*yzh/i*ifac[yzh%i]%yzh;
    for (int i = 1; i <= n; i++)
        fac[i] = 1ll*i*fac[i-1]%yzh,
        ifac[i] = 1ll*ifac[i-1]*ifac[i]%yzh;
    for (int i = 1; i <= n; i++) scanf("%d", &x), cnt[x]++;
    while (k--) {
        scanf("%d", &x); a = b = 0;
        for (int i = 1; i < x; i++)
            if (cnt[i] >= 2) a += 2;
            else if (cnt[i] == 1) b++;
        memset(A, 0, sizeof(A));
        memset(B, 0, sizeof(B)); 
        for (int i = 0; i <= a; i++) A[i] = C(a, i);
        for (int i = 0; i <= b; i++) B[i] = 1ll*C(b, i)*quick_pow(2, i)%yzh;
        a += b; L = 0;
        for (n = 1; n <= a; n <<= 1) ++L;
        for (int i = 0; i < n; i++) R[i] = (R[i>>1]>>1)|((i&1)<<(L-1));
        NTT(A, 1), NTT(B, 1);
        for (int i = 0; i < n; i++) A[i] = 1ll*A[i]*B[i]%yzh;
        NTT(A, -1);
        int inv = quick_pow(n, yzh-2);
        for (int i = 0; i <= a; i++) A[i] = 1ll*A[i]*inv%yzh;
        for (int i = 0; i <= a; i++) 
            (ans[(x+1+i)<<1] += A[i]) %= yzh;
    }
    scanf("%d", &q);
    while (q--) scanf("%d", &x), printf("%d\n", (ans[x]+yzh)%yzh);
    return 0;
}

おすすめ

転載: www.cnblogs.com/NaVi-Awson/p/11741333.html