xjoi 2082: 小明的序列
维护一个序列,初始全为\(1\)
支持两种操作:
1.对于所有的位置\(i\),将它的值乘上\(i + a\)
2.询问\(a\)处的值
\(q=120000\) 20s 512M
——————
如果把第一个操作看成乘上一个\(x + a_i\),第二个操作看成询问\(x = a_i\)处多项式的值,那么这是一个裸的动态多点求值
首先暴力是可以AC的……恩,开个O2就行……
直接上CDQ+静态多点求值的话是\(O(n \log ^3 n)\)的,只能跑过60分
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int N = 500000; const int MOD = 802160641; const int G = 11; int powi(int a, int b) { int c = 1; for (; b; b >>= 1, a = 1ll * a * a % MOD) if (b & 1) c = 1ll * c * a % MOD; return c; } void NTT(int A[], int n, int f) { for (int i = 1; i < n; ++ i) { int j = 0; for (int p = 1, q = n >> 1; p < n; p <<= 1, q >>= 1) if (i & p) j |= q; if (i < j) swap(A[i], A[j]); } for (int i = 2; i <= n; i <<= 1) { int w1 = powi(G, (MOD - 1) / i); if (f < 0) w1 = powi(w1, MOD - 2); for (int j = 0; j < n; j += i) { int w = 1; for (int k = j; k < j + (i >> 1); ++ k) { int u = A[k], v = 1ll * A[k + (i >> 1)] * w % MOD; A[k] = (u + v) % MOD; A[k + (i >> 1)] = (u - v + MOD) % MOD; w = 1ll * w * w1 % MOD; } } } int p = powi(n, MOD - 2); if (f < 0) for (int i = 0; i < n; ++ i) A[i] = 1ll * A[i] * p % MOD; } int Rev_tmp1[N], Rev_tmp2[N]; void Rev(int A[], int G[], int n) { if (n == 1) { G[0] = powi(A[0], MOD - 2); } else { Rev(A, G, n / 2); for (int i = 0; i < n; ++ i) Rev_tmp1[i] = A[i]; for (int i = n; i < (n << 1); ++ i) Rev_tmp1[i] = 0; for (int i = 0; i < (n >> 1); ++ i) Rev_tmp2[i] = G[i]; for (int i = (n >> 1); i < (n << 1); ++ i) Rev_tmp2[i] = 0; NTT(Rev_tmp1, n << 1, 1); NTT(Rev_tmp2, n << 1, 1); for (int i = 0; i < (n << 1); ++ i) Rev_tmp1[i] = ((2 - 1ll * Rev_tmp1[i] * Rev_tmp2[i]) % MOD + MOD) * Rev_tmp2[i] % MOD; NTT(Rev_tmp1, n << 1, -1); for (int i = 0; i < n; ++ i) G[i] = Rev_tmp1[i]; } } int Mod_tmp1[N], Mod_tmp2[N], Mod_tmp3[N], Mod_tmp4[N]; void Mod(int A[], int B[], int D[], int n, int m) { if (n < m) { for (int i = 0; i < n; ++ i) D[i] = A[i]; return; } int nn = 1; while (nn < (n - m + 1)) nn <<= 1; for (int i = 0; i < n; ++ i) Mod_tmp3[i] = A[i]; for (int i = 0; i < m; ++ i) Mod_tmp4[i] = B[i]; for (int i = 0, j = n - 1; i < j; ++ i, -- j) swap(Mod_tmp3[i], Mod_tmp3[j]); for (int i = 0, j = m - 1; i < j; ++ i, -- j) swap(Mod_tmp4[i], Mod_tmp4[j]); for (int i = m; i < nn; ++ i) Mod_tmp4[i] = 0; Rev(Mod_tmp4, Mod_tmp2, nn); for (int i = 0; i < n - m + 1; ++ i) Mod_tmp1[i] = Mod_tmp3[i]; for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp1[i] = 0; for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp2[i] = 0; NTT(Mod_tmp1, nn << 1, 1); NTT(Mod_tmp2, nn << 1, 1); for (int i = 0; i < (nn << 1); ++ i) Mod_tmp1[i] = 1ll * Mod_tmp1[i] * Mod_tmp2[i] % MOD; NTT(Mod_tmp1, nn << 1, -1); while (nn < n) nn <<= 1; for (int i = 0, j = n - m; i < j; ++ i, -- j) swap(Mod_tmp1[i], Mod_tmp1[j]); for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp1[i] = 0; for (int i = 0; i < m; ++ i) Mod_tmp2[i] = B[i]; for (int i = m; i < (nn << 1); ++ i) Mod_tmp2[i] = 0; NTT(Mod_tmp1, nn << 1, 1); NTT(Mod_tmp2, nn << 1, 1); for (int i = 0; i < (nn << 1); ++ i) Mod_tmp1[i] = 1ll * Mod_tmp1[i] * Mod_tmp2[i] % MOD; NTT(Mod_tmp1, nn << 1, -1); for (int i = 0; i < m - 1; ++ i) D[i] = (A[i] - Mod_tmp1[i] + MOD) % MOD; } int AAA[N]; int Mul_tmp[20][N], Mul_tmp1[N], Mul_tmp2[N]; void Mul(int x, int l, int r) { if (r - l == 1) { Mul_tmp[x][l * 2 + 1] = 1; Mul_tmp[x][l * 2] = AAA[l]; return; } int m = (l + r) / 2; Mul(x + 1, l, m); Mul(x + 1, m, r); for (int i = 0; i < ((m - l) << 1); ++ i) Mul_tmp1[i] = Mul_tmp[x + 1][i + l * 2]; for (int i = 0; i < ((r - m) << 1); ++ i) Mul_tmp2[i] = Mul_tmp[x + 1][i + m * 2]; int nn = 1; while (nn < (r - l) * 2) nn <<= 1; for (int i = ((m - l) << 1); i < nn; ++ i) Mul_tmp1[i] = 0; for (int i = ((r - m) << 1); i < nn; ++ i) Mul_tmp2[i] = 0; NTT(Mul_tmp1, nn, 1); NTT(Mul_tmp2, nn, 1); for (int i = 0; i < nn; ++ i) Mul_tmp1[i] = 1ll * Mul_tmp1[i] * Mul_tmp2[i] % MOD; NTT(Mul_tmp1, nn, -1); for (int i = 0; i < ((r - l) << 1); ++ i) Mul_tmp[x][i + l * 2] = Mul_tmp1[i]; } int get_val_tmp[20][N]; void get_val(int fin[], int x, int l, int r) { if (r - l == 1) { fin[l] = get_val_tmp[x][l * 2]; return; } int m = (l + r) / 2; Mod(get_val_tmp[x] + l * 2, Mul_tmp[x + 1] + l * 2, get_val_tmp[x + 1] + l * 2, (r - l) * 2, (m - l) + 1); Mod(get_val_tmp[x] + l * 2, Mul_tmp[x + 1] + m * 2, get_val_tmp[x + 1] + m * 2, (r - l) * 2, (r - m) + 1); for (int i = m + l; i < m * 2; ++ i) get_val_tmp[x + 1][i] = 0; for (int i = r + m; i < r * 2; ++ i) get_val_tmp[x + 1][i] = 0; get_val(fin, x + 1, l, m); get_val(fin, x + 1, m, r); } void GGG(int A[], int B[], int D[], int n, int m) { for (int i = 0; i < m * 2; ++ i) get_val_tmp[0][i] = 0; for (int i = 0; i < m; ++ i) AAA[i] = MOD - B[i]; Mul(0, 0, m); Mod(A, Mul_tmp[0], get_val_tmp[0], n, m + 1); get_val(D, 0, 0, m); } int typ[N], val[N], ans[N]; int solve_tmp1[N], solve_tmp2[N], solve_tmp3[N]; void solve(int l, int r) { if (r - l == 1) return; int m = (l + r) / 2; solve(l, m); solve(m, r); int tot1 = 0, tot2 = 0, tot3 = 0; for (int i = l; i < m; ++ i) if (typ[i] == 2) AAA[tot1 ++] = val[i]; for (int i = m; i < r; ++ i) if (typ[i] == 1) solve_tmp2[tot2 ++] = val[i]; if (!tot1 || !tot2) return; Mul(0, 0, tot1); for (int i = 0; i < tot1 + 1; ++ i) solve_tmp1[i] = Mul_tmp[0][i]; for (int i = tot1 + 1; i < tot1 * 3; ++ i) solve_tmp1[i] = 0; GGG(solve_tmp1, solve_tmp2, solve_tmp3, tot1 + 1, tot2); for (int i = m; i < r; ++ i) if (typ[i] == 1) ans[i] = 1ll * ans[i] * solve_tmp3[tot3 ++] % MOD; } int main() { int a, b, c, n; scanf("%d%d%d", &a, &b, &c); n = b + c; for (int i = 0; i < n; ++ i) { scanf("%d%d", &typ[i], &val[i]); ans[i] = 1; val[i] = ((val[i]) % MOD + MOD) % MOD; } solve(0, n); for (int i = 0; i < n; ++ i) if (typ[i] == 1) printf("%d\n", ans[i]); }
令第\(i-1\)次询问到第\(i\)次询问之间的多项式之积为\(A_i(x)\)
那么显然有 \(ans_i = (\prod_{p=1}^{i} A_p(x)) \mod (x - a_i)\)
令\(f_{l,r}=(\prod_{p=1}^{l} A_p(x)) \mod (\prod_{p = l}^{r-1} (x - a_p)) \)
于是\(ans_i = f_{i,i+1}\)
注意到
\(f_{l,mid} = f_{l,r} \mod (\prod_{p = l}^{mid-1} (x - a_p)) \)
\(f_{mid,r} =( f_{l,r} \prod_{p=l+1}^{mid} A_p(x)) \mod (\prod_{p = mid}^{r-1} (x - a_p)) \)
\(f_{1,q}=A_1(x) \)
预处理 需要用的 \(x-a_i\)的积 和 需要用的 \(A_i(x) \) 的积(就是一个多项式分治乘法啦
一次递归只需要跑一次多项式乘法和两次多项式取膜,多项式的度数是\(O(len)\)的,因此一次递归的复杂度是\(O(len \log len)\)的
\(T(n) = 2 T(\frac{n}{2}) + O(n \log n))\)
最终复杂度\(O(n \log ^2 n)\)
代码写的和上面的分析有些不同,所以有点萎(捂脸
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int N = 1000000; const int MOD = 802160641; const int G = 11; int powi(int a, int b) { int c = 1; for (; b; b >>= 1, a = 1ll * a * a % MOD) if (b & 1) c = 1ll * c * a % MOD; return c; } void NTT(int A[], int n, int f) { for (int i = 0, j = 0; i < n; ++ i) { if (i > j) swap(A[i], A[j]); for (int l = n >> 1; (j ^= l) < l; l >>= 1); } for (int i = 2; i <= n; i <<= 1) { int w1 = powi(G, (MOD - 1) / i); if (f < 0) w1 = powi(w1, MOD - 2); for (int j = 0; j < n; j += i) { int w = 1; for (int k = j; k < j + (i >> 1); ++ k) { int u = A[k], v = 1ll * A[k + (i >> 1)] * w % MOD; A[k] = (u + v >= MOD? u + v - MOD: u + v); A[k + (i >> 1)] = (u - v < 0? u - v + MOD: u - v); w = 1ll * w * w1 % MOD; } } } int p = powi(n, MOD - 2); if (f < 0) for (int i = 0; i < n; ++ i) A[i] = 1ll * A[i] * p % MOD; } int Rev_tmp1[N], Rev_tmp2[N]; void Rev(int A[], int G[], int n) { if (n == 1) { G[0] = powi(A[0], MOD - 2); } else { Rev(A, G, n / 2); for (int i = 0; i < n; ++ i) Rev_tmp1[i] = A[i]; for (int i = n; i < (n << 1); ++ i) Rev_tmp1[i] = 0; for (int i = 0; i < (n >> 1); ++ i) Rev_tmp2[i] = G[i]; for (int i = (n >> 1); i < (n << 1); ++ i) Rev_tmp2[i] = 0; NTT(Rev_tmp1, n << 1, 1); NTT(Rev_tmp2, n << 1, 1); for (int i = 0; i < (n << 1); ++ i) Rev_tmp1[i] = ((2 - 1ll * Rev_tmp1[i] * Rev_tmp2[i]) % MOD + MOD) * Rev_tmp2[i] % MOD; NTT(Rev_tmp1, n << 1, -1); for (int i = 0; i < n; ++ i) G[i] = Rev_tmp1[i]; } } int Mod_tmp1[N], Mod_tmp2[N], Mod_tmp3[N], Mod_tmp4[N]; void Mod(int A[], int B[], int D[], int n, int m) { while (B[m - 1] == 0) m --; if (n < m) { for (int i = 0; i < n; ++ i) D[i] = A[i]; return; } int nn = 1; while (nn < (n - m + 1)) nn <<= 1; for (int i = 0; i < n; ++ i) Mod_tmp3[i] = A[i]; for (int i = 0; i < m; ++ i) Mod_tmp4[i] = B[i]; for (int i = 0, j = n - 1; i < j; ++ i, -- j) swap(Mod_tmp3[i], Mod_tmp3[j]); for (int i = 0, j = m - 1; i < j; ++ i, -- j) swap(Mod_tmp4[i], Mod_tmp4[j]); for (int i = m; i < nn; ++ i) Mod_tmp4[i] = 0; Rev(Mod_tmp4, Mod_tmp2, nn); for (int i = 0; i < n - m + 1; ++ i) Mod_tmp1[i] = Mod_tmp3[i]; for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp1[i] = 0; for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp2[i] = 0; NTT(Mod_tmp1, nn << 1, 1); NTT(Mod_tmp2, nn << 1, 1); for (int i = 0; i < (nn << 1); ++ i) Mod_tmp1[i] = 1ll * Mod_tmp1[i] * Mod_tmp2[i] % MOD; NTT(Mod_tmp1, nn << 1, -1); while (nn < n) nn <<= 1; for (int i = 0, j = n - m; i < j; ++ i, -- j) swap(Mod_tmp1[i], Mod_tmp1[j]); for (int i = n - m + 1; i < (nn << 1); ++ i) Mod_tmp1[i] = 0; for (int i = 0; i < m; ++ i) Mod_tmp2[i] = B[i]; for (int i = m; i < (nn << 1); ++ i) Mod_tmp2[i] = 0; NTT(Mod_tmp1, nn << 1, 1); NTT(Mod_tmp2, nn << 1, 1); for (int i = 0; i < (nn << 1); ++ i) Mod_tmp1[i] = 1ll * Mod_tmp1[i] * Mod_tmp2[i] % MOD; NTT(Mod_tmp1, nn << 1, -1); for (int i = 0; i < m - 1; ++ i) D[i] = (A[i] - Mod_tmp1[i] + MOD) % MOD; } int AAA[N], BBB[N]; int Mul1[20][N], Mul2[20][N], Mul_tmp1[N], Mul_tmp2[N]; void Mul(int Mul_tmp[][N], int x, int l, int r) { if (r - l == 1) { if (!BBB[l]) { Mul_tmp[x][l * 2 + 1] = 1; Mul_tmp[x][l * 2] = AAA[l]; } else { Mul_tmp[x][l * 2 + 1] = 0; Mul_tmp[x][l * 2] = 1; } return; } int m = (l + r) / 2; Mul(Mul_tmp, x + 1, l, m); Mul(Mul_tmp, x + 1, m, r); for (int i = 0; i < ((m - l) << 1); ++ i) Mul_tmp1[i] = Mul_tmp[x + 1][i + l * 2]; for (int i = 0; i < ((r - m) << 1); ++ i) Mul_tmp2[i] = Mul_tmp[x + 1][i + m * 2]; int nn = 1; while (nn < (r - l) * 2) nn <<= 1; for (int i = ((m - l) << 1); i < nn; ++ i) Mul_tmp1[i] = 0; for (int i = ((r - m) << 1); i < nn; ++ i) Mul_tmp2[i] = 0; NTT(Mul_tmp1, nn, 1); NTT(Mul_tmp2, nn, 1); for (int i = 0; i < nn; ++ i) Mul_tmp1[i] = 1ll * Mul_tmp1[i] * Mul_tmp2[i] % MOD; NTT(Mul_tmp1, nn, -1); for (int i = 0; i < ((r - l) << 1); ++ i) Mul_tmp[x][i + l * 2] = Mul_tmp1[i]; } int cnt1[N], cnt2[N]; int ans[N], typ[N], val[N]; int solve_tmp[20][N], solve_tmp1[N], solve_tmp2[N]; void solve(int x, int l, int r) { if (r - l == 1) { ans[l] = solve_tmp[x][l * 2]; return; } if (cnt1[r] - cnt1[l] == 0) return; int m = (l + r) / 2; Mod(solve_tmp[x] + l * 2, Mul1[x + 1] + l * 2, solve_tmp[x + 1] + l * 2, (r - l) * 2, (m - l) * 2); int nn = 1; while (nn < (r - l) * 2) nn *= 2; for (int i = 0; i < (r - l) * 2; ++ i) solve_tmp1[i] = solve_tmp[x][i + l * 2]; for (int i = (r - l) * 2; i < nn; ++ i) solve_tmp1[i] = 0; for (int i = 0; i < (m - l) * 2; ++ i) solve_tmp2[i] = Mul2[x + 1][i + l * 2]; for (int i = (m - l) * 2; i < nn; ++ i) solve_tmp2[i] = 0; NTT(solve_tmp1, nn, 1); NTT(solve_tmp2, nn, 1); for (int i = 0; i < nn; ++ i) solve_tmp1[i] = 1ll * solve_tmp1[i] * solve_tmp2[i] % MOD; NTT(solve_tmp1, nn, -1); Mod(solve_tmp1, Mul1[x + 1] + m * 2, solve_tmp[x + 1] + m * 2, (r - l) * 2, (r - m) * 2); solve(x + 1, l, m); solve(x + 1, m, r); } namespace IO { const int SIZE=(int)(1e6); char buf[SIZE]; FILE *in,*out; int cur; inline void init() { cur=SIZE;in=stdin;out=stdout; } inline char nextChar() { if(cur==SIZE) { fread(buf,SIZE,1,in);cur=0; } return buf[cur++]; } inline int nextInt() { bool st=0,neg=0; char c;int num=0; while((c=nextChar())!=EOF) { if(c=='-') st=neg=1; else if(c>='0' && c<='9') { st=1;num=num*10+c-'0'; } else if(st) break; } if(neg) num=-num; return num; } inline void printChar(char c) { buf[cur++]=c; if(cur==SIZE) { fwrite(buf,SIZE,1,out);cur=0; } } inline void printInt(int x) { if(x<0) { printChar('-');printInt(-x); return; } if(x>=10) printInt(x/10);printChar('0'+x%10); } inline void close() { if(cur>0) fwrite(buf,cur,1,out);cur=0; } } int main() { using namespace IO; int a, b, c, n; init(); a = nextInt(); b = nextInt(); c = nextInt(); n = b + c; for (int i = 0; i < n; ++ i) { typ[i] = nextInt(); val[i] = nextInt(); ans[i] = 1; cnt1[i + 1] = cnt1[i] + (typ[i] == 1); cnt2[i + 1] = cnt2[i] + (typ[i] == 2); val[i] = ((val[i]) % MOD + MOD) % MOD; } for (int i = 0; i < n; ++ i) if (typ[i] == 2) BBB[i] = 0, AAA[i] = val[i]; else BBB[i] = 1; Mul(Mul2, 0, 0, n); for (int i = 0; i < n; ++ i) if (typ[i] == 1) BBB[i] = 0, AAA[i] = MOD - val[i]; else BBB[i] = 1; Mul(Mul1, 0, 0, n); solve_tmp[0][0] = 1; solve(0, 0, n); cur = 0; for (int i = 0; i < n; ++ i) if (typ[i] == 1) printInt(ans[i]), printChar('\n'); close(); }
换了一个NTT板子,终于跑的比暴力快了QAQ
学会了多项式技巧(划去