题库连接c++
给你 \(n\) 块白木板,\(k\) 块红木板,分别有各自的长度 \(h_i\)。让你用这些木板组成一段围栏,要知足:ui
如今给出 \(q\) 组询问,问周长为 \(x_i\) 的围栏有多少种。spa
\(1\leq n,h_i,q\leq 3\cdot 10^5,4\leq x_i\leq 12\cdot 10^5,1\leq k\leq 5\)code
若是咱们选的红木板长度为 \(L\),而且这段围栏由 \(m\) 块板子构成,显然周长为 \(2\times (L+m)\)。ip
那么问题就转化为了,求用 \(m-1\) 块长度 \(<L\) 的白木板造成两段长度严格单调递增序列的方案数。get
由于木板长度是离散的,咱们能够考虑每种长度的木板的放法。it
咱们把全部长度 \(<L\) 的白木板取出。若某种长度的木板只有一块。那么显然,这块木板能够放在红木板的左边或右边(即任意一个序列中)。io
对于某种长度有两块以上的时候,咱们能够把他放在左边、右边或者两边都放。而且咱们最多只会用 \(2\) 块这样的木板,因此多余的能够除去。class
假设第一种状况(该长度的木板只有一块)下的木板个数为 \(sa\)。显然用这 \(sa\) 块木板构成两段序列总长度为 \(i\) 的方案数 \(a_i={sa \choose i}\times 2^i\)。im
假设第二种状况下的木板个数为 \(sb\)(除去多余的木板)。用这 \(sb\) 块木板构成两段序列总长度为 \(i\) 的的方案数 \(b_i={sb \choose i}\)。
记两种状况长度总和为 \(i\) 的方案数为 \(c_i\),那么容易发现咱们要求的就是
\[ c_{m-1}=\sum_{i=0}^{m-1}a_ib_{m-1-i} \]
这是一个卷积式,那么咱们设
\[ \begin{aligned} 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 \end{aligned} \]
那么咱们就能够用 \(\text{NTT}\) 来求出选该红木板时,对应选不一样白木板个数的方案数了。
所以咱们能够枚举每一个红木板,作一次 \(\text{NTT}\) 累计到答案中,\(O(1)\) 回答询问。
总复杂度 \(O(k\times n\log 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; }