SDOI2019 快速查询
考虑维护双标记,题目的难点在于如何维护单点赋值操作。
推式子发现,直接修改原本的值变为$\frac{x-add}{mul}$,用hash维护下标即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define MOD 10000019 4 namespace hash{ 5 #define sz 299999 6 int h[300010], num[300010], clo[300010]; 7 int All, CLO; 8 inline int get_Num(int x) { 9 int wt = x % sz; 10 while(num[wt] != x && num[wt]) { 11 ++ wt; 12 if(wt == sz) wt = 0; 13 } 14 num[wt] = x; 15 return wt; 16 } 17 inline int upd(int x, int y, int CL) { 18 int wt = get_Num(x); 19 if(clo[wt] < CLO) h[wt] = All; 20 int ret = y - h[wt]; 21 h[wt] = y; clo[wt] = CL; 22 return ret; 23 } 24 inline int query(int x) { 25 int wt = get_Num(x); 26 if(clo[wt] < CLO) h[wt] = All, clo[wt] = CLO; 27 return h[wt]; 28 } 29 } 30 int inv[MOD+10]; 31 inline void init() { 32 inv[0] = inv[1] = 1; 33 for(int i = 2; i < MOD; ++ i) { 34 inv[i] = 1ll * inv[MOD % i] * (MOD - MOD / i) % MOD; 35 } 36 } 37 struct Node { 38 int op, x, y; 39 } que[100010]; 40 int main() { 41 //freopen("a.in", "r", stdin); 42 init(); 43 int n, q; 44 scanf("%d%d", &n, &q); 45 for(int i = 1; i <= q; ++ i) { 46 int op, x = 0, y = 0; 47 scanf("%d", &op); 48 if(op == 1) { 49 scanf("%d%d", &x, &y); 50 y %= MOD; 51 y = (y + MOD) % MOD; 52 } 53 else if(2 <= op && op <= 5) { 54 scanf("%d", &x); 55 if(op != 5) { 56 x %= MOD; 57 x = (x + MOD) % MOD; 58 } 59 } 60 //cerr << op << " " << x << " " << y << endl; 61 que[i] = (Node){op, x, y}; 62 } 63 int mul = 1, add = 0, sum = 0; 64 int t; 65 scanf("%d", &t); 66 int Ans = 0, cnt = 0; 67 while(t --) { 68 int A, B; 69 scanf("%d%d", &A, &B); 70 //cerr << A << B << endl; 71 for(int i = 1; i <= q; ++ i) { 72 ++ cnt; 73 int id = (A + 1ll * i * B) % q + 1; 74 int op = que[id].op, x = que[id].x, y = que[id].y; 75 if(op == 1) { 76 int ry = 1ll * (y - add + MOD) * inv[mul] % MOD; 77 int d = hash::upd(x, ry, cnt); 78 sum += d; 79 if(sum >= MOD) sum -= MOD; 80 if(sum < 0) sum += MOD; 81 } 82 else if(op == 2) { 83 add += x; 84 if(add >= MOD) add -= MOD; 85 } 86 else if(op == 4 || (op == 3 && x == 0)) { 87 sum = 1ll * n * x % MOD; 88 add = 0, mul = 1; 89 hash::All = (x + MOD) % MOD; 90 hash::CLO = cnt; 91 } 92 else if(op == 3) { 93 mul = 1ll * mul * x % MOD; 94 add = 1ll * add * x % MOD; 95 } 96 else if(op == 5) { 97 int res = hash::query(x); 98 res = (1ll * res * mul + add) % MOD; 99 Ans += res; 100 if(Ans >= MOD) Ans -= MOD; 101 } 102 else { 103 int res = (1ll * sum * mul + 1ll * n * add) % MOD; 104 Ans += res; 105 if(Ans >= MOD) Ans -= MOD; 106 } 107 if(mul < 0) mul += MOD; 108 if(add < 0) add += MOD; 109 if(sum < 0) sum += MOD; 110 //cerr << sum << endl; 111 } 112 } 113 printf("%d\n", Ans); 114 }
SDOI2019 热闹的聚会与尴尬的聚会
一道构造好题。
$\lfloor\frac{n}{p+1}\rfloor \leq q$可以推出$(p+1)(q+1)>n$。
每次枚举当前图中度数最小的点$x$,删除$x$以及与$x$相邻的点。
设总共会删除$q$次,显然有$\sum_{i=1}^q (d_i+1) = n$。
对于每次枚举的$x$度数中的最大值$mxd$,一定可以找到一个$p=mxd$的构造方案。
所以有$\sum_{i=1}^q (d_i+1)\leq mxd*q$。
稍加推倒可以证明如果找到$p=mxd$的构造方案,那么可以满足$(p+1)(q+1)>n$。
构造的方案即为当$d_x=mxd$时,当前图中剩余的全部点。
显然可以发现除了$x$以外,其他的点$y$满足$mxd \leq d_y$。
用$set$维护即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct Edge{ 4 int u, v, Next; 5 } G[200010]; 6 int head[100010], tot; 7 int d[100010]; 8 struct Node{ 9 int D, x; 10 inline bool operator < (const Node& rhs) const{ 11 return D < rhs.D || (D == rhs.D && x < rhs.x); 12 } 13 }; 14 set<Node> S; 15 inline void add(int u, int v) { 16 G[++ tot] = (Edge){u, v, head[u]}; 17 head[u] = tot; 18 } 19 int ans1[100010], t1; 20 int ans2[100010], t2; 21 inline void solve() { 22 int n, m; 23 scanf("%d%d", &n, &m); 24 tot = 0; 25 for(int i = 1; i <= n; ++ i) { 26 head[i] = -1; 27 d[i] = 0; 28 } 29 for(int i = 1; i <= m; ++ i) { 30 int u, v; 31 scanf("%d%d", &u, &v); 32 add(u, v), add(v, u); 33 ++ d[u], ++ d[v]; 34 } 35 for(int i = 1; i <= n; ++ i) { 36 S.insert((Node){d[i], i}); 37 } 38 int mxd = 0, pos; 39 t1 = t2 = 0; 40 while(!S.empty()) { 41 set<Node> :: iterator t = S.begin(); 42 //cerr << "element " << t -> x << endl; 43 S.erase(t); 44 if(t -> D > mxd) { 45 mxd = t -> D; 46 pos = t1; 47 } 48 int x = t -> x; 49 ans2[++ t2] = x; 50 ans1[++ t1] = x; 51 for(int i = head[x]; i != -1; i = G[i].Next) { 52 t = S.find((Node){d[G[i].v], G[i].v}); 53 54 if(t == S.end()) continue; 55 //cerr << x << " " << G[i].v << endl; 56 int X = t -> x; 57 ans1[++ t1] = X; 58 S.erase(t); 59 for(int j = head[X]; j != -1; j = G[j].Next) { 60 set<Node> :: iterator it = S.find((Node){d[G[j].v], G[j].v}); 61 if(it == S.end()) continue; 62 S.erase(it); 63 S.insert((Node){-- d[G[j].v], G[j].v}); 64 } 65 } 66 } 67 printf("%d ", t1 - pos); 68 for(int i = pos + 1; i <= t1; ++ i) { 69 printf("%d ", ans1[i]); 70 } 71 puts(""); 72 printf("%d ", t2); 73 for(int i = 1; i <= t2; ++ i) { 74 printf("%d ", ans2[i]); 75 } 76 puts(""); 77 } 78 int main() { 79 int T; 80 scanf("%d", &T); 81 while(T --) { 82 solve(); 83 } 84 }
SDOI2019 移动金币
首先进行模型转化。题目等价于:
有$m+1$堆棋子,棋子总数$n-m$,每次把一堆中的若干棋子移动到前一堆中,求先手必胜方案数。
也就是求阶梯$NIM$的先手必胜方案数,即奇数项异或和不为$0$的方案数。
容斥一下,变为询问异或和为$0$的方案数。
$f_{i,j}$表示前$i$位确定,和为$j$的方案数。
$g_{i,j,k}$表示前$i$个堆,和为$j$,异或和为$k$的方案数。
注意本题中第一堆棋子是无用的,即为一开始就在第$0$堆中,需从第二堆开始编号。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define MOD 1000000009 4 #define mxbit 19 5 int f[60][150010]; // first i bit sum = j 6 int g[60][60][2]; // first i pile sum = j xor_sum = k; 7 inline void Add(int &x, int y) { 8 x += y; 9 if(x >= MOD) x -= MOD; 10 } 11 inline int Power(int x, int y) { 12 int ret = 1; 13 while(y) { 14 if(y & 1) ret = 1ll * ret * x % MOD; 15 x = 1ll * x * x % MOD; 16 y >>= 1; 17 } 18 return ret; 19 } 20 inline int C(int n, int m) { 21 int ret = 1; 22 for(int i = n - m + 1; i <= n; ++ i) { 23 ret = 1ll * ret * i % MOD; 24 } 25 for(int i = 1; i <= m; ++ i) { 26 ret = 1ll * ret * Power(i, MOD - 2) % MOD; 27 } 28 return ret; 29 } 30 inline void calc_g(int m, int n) { 31 g[0][0][0] = 1; 32 for(int i = 0; i < m; ++ i) { 33 for(int j = 0; j <= i; ++ j) { 34 for(int k = 0; k <= 1; ++ k) { 35 if(i & 1) { 36 for(int w = 0; w <= 1; ++ w) { 37 Add(g[i + 1][j + w][k ^ w], g[i][j][k]); 38 } 39 } 40 else { 41 for(int w = 0; w <= 1; ++ w) { 42 Add(g[i + 1][j + w][k], g[i][j][k]); 43 } 44 } 45 } 46 } 47 } 48 } 49 inline void calc_f(int m, int n) { 50 f[0][0] = 1; 51 for(int i = 0; i < mxbit; ++ i) { 52 for(int j = 0; j <= n; ++ j) if(f[i][j]) { 53 for(int k = 0; k <= m && k * (1 << i) + j <= n; ++ k) { 54 Add(f[i + 1][j + k * (1 << i)], 1ll * f[i][j] * g[m][k][0] % MOD); 55 } 56 } 57 } 58 } 59 int main() { 60 int n, m; 61 scanf("%d%d", &n, &m); 62 int All = C(n, m); 63 calc_g(m + 1, n - m); 64 calc_f(m + 1, n - m); 65 All -= f[mxbit][n - m]; 66 if(All < 0) All += MOD; 67 printf("%d\n", All); 68 }
SDOI2017 数字表格
题目就是要求$$\prod_d^{n} f(d)^{g(d,n,m)}$$
其中$$g(d,n,m)=\sum_i^{\lfloor\frac{n}{d}\rfloor}\sum_j^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j)=1]$$
显然莫比乌斯反演得到$$\prod_d^{n} f(d)^{\sum_k^{\lfloor\frac{n}{d}\rfloor} \lfloor\frac{n}{kd}\rfloor\lfloor\frac{m}{kd}\rfloor \mu(k)}$$
令$u=kd$换元得到$$\prod_u^n \prod_{d|u} f(d)^{\lfloor\frac{n}{u}\rfloor\lfloor\frac{m}{u}\rfloor\mu(\frac{u}{d})}$$
预处理后每次$O(\sqrt{n})$求答案
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define M 1000010 4 #define MOD 1000000007 5 int mu[M], pri[M], tot; 6 bool vis[M]; 7 int f[M]; 8 int pre[M], inv[M], g[M]; 9 inline int Power(int x, int y) { 10 int ret = 1; 11 while(y) { 12 if(y & 1) ret = 1ll * ret * x % MOD; 13 x = 1ll * x * x % MOD; 14 y >>= 1; 15 } 16 return ret; 17 } 18 inline void init() { 19 mu[1] = 1; 20 f[1] = 1; 21 for(int i = 2; i <= M - 10; ++ i) { 22 f[i] = f[i - 1] + f[i - 2]; 23 if(f[i] >= MOD) f[i] -= MOD; 24 } 25 for(int i = 2; i <= M - 10; ++ i) { 26 if(!vis[i]) { 27 pri[++ tot] = i; 28 mu[i] = -1; 29 } 30 for(int j = 1; j <= tot; ++ j) { 31 if(i * pri[j] > M - 10) { 32 break; 33 } 34 vis[i * pri[j]] = 1; 35 if(i % pri[j] == 0) { 36 mu[i * pri[j]] = 0; 37 break; 38 } 39 else mu[i * pri[j]] = -mu[i]; 40 } 41 } 42 pre[0] = inv[0] = 1; 43 for(int i = 1; i <= M - 10; ++ i) { 44 pre[i] = 1; 45 } 46 for(int i = 1; i <= M - 10; ++ i) { 47 int invf = Power(f[i], MOD - 2); 48 for(int j = i; j <= M - 10; j += i) { 49 if(mu[j / i] == -1) { 50 pre[j] = 1ll * pre[j] * invf % MOD; 51 } 52 else if(mu[j / i] == 1) { 53 pre[j] = 1ll * pre[j] * f[i] % MOD; 54 } 55 } 56 } 57 for(int i = 1; i <= M - 10; ++ i) { 58 pre[i] = 1ll * pre[i - 1] * pre[i] % MOD; 59 inv[i] = Power(pre[i], MOD - 2); 60 } 61 } 62 inline void solve(int n, int m) { 63 int res = 1, lst; 64 for(int i = 1; i <= n && i <= m; i = lst + 1) { 65 lst = min(n / (n / i), m / (m / i)); 66 int tmp = 1ll * (n / i) * (m / i) % (MOD - 1); 67 res = 1ll * res * Power(1ll * pre[lst] * inv[i - 1] % MOD, tmp) % MOD; 68 } 69 printf("%d\n", res); 70 } 71 int main() { 72 init(); 73 int T; 74 scanf("%d", &T); 75 while(T --) { 76 int n, m; 77 scanf("%d%d", &n, &m); 78 solve(n, m); 79 } 80 }
SDOI2017 序列计数
题目比较基础,容斥之后就变成了矩阵优化dp
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define MOD 20170408 4 bool vis[20000010]; 5 int pri[2000010], tot; 6 int a[500]; // with prime 7 int b[500]; // without prime 8 int n, m, p; 9 inline void init() { 10 vis[1] = 1; 11 for(int i = 2; i <= m; ++ i) { 12 if(!vis[i]) { 13 pri[++ tot] = i; 14 } 15 for(int j = 1; j <= tot; ++ j) { 16 if(i * pri[j] > m) break; 17 vis[i * pri[j]] = 1; 18 if(i % pri[j] == 0) break; 19 } 20 } 21 int q = 0; 22 for(int i = 1; i <= m; ++ i) { 23 ++ q; 24 if(q == p) q = 0; 25 ++ a[q]; 26 if(vis[i]) ++ b[q]; 27 } 28 for(int i = 0; i < p; ++ i) { 29 a[i] %= MOD; 30 b[i] %= MOD; 31 } 32 } 33 int o[110][110]; 34 int f[110], g[110]; 35 int c[110], C[110][110]; 36 inline void Do(int *F, int *a) { 37 F[0] = 1; 38 memset(o, 0, sizeof(o)); 39 for(int i = 0; i < p; ++ i) { 40 for(int j = 0; j < p; ++ j) { 41 o[i][j] = a[(j - i + p) % p]; 42 } 43 } 44 int y = n; 45 while(y) { 46 if(y & 1) { 47 for(int i = 0; i < p; ++ i) { 48 c[i] = 0; 49 } 50 for(int i = 0; i < p; ++ i) { 51 for(int j = 0; j < p; ++ j) { 52 c[j] += 1ll * F[i] * o[i][j] % MOD; 53 if(c[j] >= MOD) c[j] -= MOD; 54 } 55 } 56 for(int i = 0; i < p; ++ i) { 57 F[i] = c[i]; 58 } 59 } 60 y >>= 1; 61 for(int i = 0; i < p; ++ i) { 62 for(int j = 0; j < p; ++ j) { 63 C[i][j] = 0; 64 for(int k = 0; k < p; ++ k) { 65 C[i][j] += 1ll * o[i][k] * o[k][j] % MOD; 66 } 67 C[i][j] %= MOD; 68 } 69 } 70 for(int i = 0; i < p; ++ i) { 71 for(int j = 0; j < p; ++ j) { 72 o[i][j] = C[i][j]; 73 } 74 } 75 } 76 } 77 int main() { 78 scanf("%d%d%d", &n, &m, &p); 79 init(); 80 Do(f, a), Do(g, b); 81 printf("%d\n", (f[0] - g[0] + MOD) % MOD); 82 }
一个人的高三楼
题目就是要求$A*B^k$,$B(x)=1+x+x^2+……+x^n$
注意到$B^k$可以用组合数计算,套个NTT就行
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define M 1000010 4 #define LL long long 5 #define MOD 998244353 6 inline int Power(int x, int y) { 7 int ret = 1; 8 while(y) { 9 if(y & 1) ret = 1ll * ret * x % MOD; 10 x = 1ll * x * x % MOD; 11 y >>= 1; 12 } 13 return ret; 14 } 15 int N; 16 int a[M], b[M], rev[M], w[M]; 17 inline void NTT(int *a) { 18 for(int i = 0; i < N; ++ i) { 19 if(rev[i] > i) { 20 swap(a[rev[i]], a[i]); 21 } 22 } 23 for(int d = 1, t = (N >> 1); d < N; d <<= 1, t >>= 1) { 24 for(int i = 0; i < N; i += (d << 1)) { 25 for(int j = 0; j < d; ++ j) { 26 int tmp = 1ll * w[t * j] * a[i + j + d] % MOD; 27 a[i + j + d] = (a[i + j] - tmp + MOD) % MOD; 28 a[i + j] = (a[i + j] + tmp) % MOD; 29 } 30 } 31 } 32 } 33 int main() { 34 int n; LL K; 35 scanf("%d%lld", &n, &K); 36 for(int i = 0; i < n; ++ i) { 37 scanf("%d", &a[i]); 38 } 39 b[0] = 1; 40 for(int i = 1; i < n; ++ i) { 41 b[i] = 1ll * b[i - 1] * ((K + i - 1) % MOD) % MOD * Power(i, MOD - 2) % MOD; 42 } 43 N = 1; int L = 0; 44 for(; N <= 2 * n; N <<= 1, ++ L); 45 for(int i = 0; i < N; ++ i) { 46 rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1)); 47 } 48 w[0] = 1; w[1] = Power(3, (MOD - 1) / N); 49 for(int i = 2; i < N; ++ i) { 50 w[i] = 1ll * w[i - 1] * w[1] % MOD; 51 } 52 NTT(a), NTT(b); 53 for(int i = 0; i < N; ++ i) { 54 a[i] = 1ll * a[i] * b[i] % MOD; 55 } 56 w[1] = Power(w[1], MOD - 2); 57 for(int i = 2; i < N; ++ i) { 58 w[i] = 1ll * w[i - 1] * w[1] % MOD; 59 } 60 NTT(a); 61 int inv = Power(N, MOD - 2); 62 for(int i = 0; i < N; ++ i) { 63 a[i] = 1ll * a[i] * inv % MOD; 64 } 65 for(int i = 0; i < n; ++ i) { 66 printf("%d\n", a[i]); 67 } 68 }
TJOI2017 唱、跳、rap和篮球
容斥之后,就是求选$i$个连续四个位置的方案数$f(i)$乘剩下的数的选择方案$g(n-4*i,i)$。
连续四个就等于$n-3*i$的数列中选$i$个,再把这$i$个后面每个都填上$3$个。
所以有$f(i)=C(n-3*i,i)$。
$g(n-4*t,t)=[x^{n-4*t}]\sum_{i=0}^{a-t}\sum_{j=0}^{b-t}\sum_{k=0}^{c-t}\sum_{w=0}^{d-t}\frac{n!}{i!j!k!w!}*x^{i+j+k+w}$
$NTT$优化复杂度$O(n^2logn)$
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define MOD 998244353 4 int n, A, B, C, D; 5 int lim; 6 int fac[1010], inv[1010], f[1010]; 7 int N; 8 int rev[10010]; 9 int w[10010], a[10010], b[10010], c[10010], d[10010]; 10 inline int Power(int x, int y) { 11 int ret = 1; 12 while(y) { 13 if(y & 1) ret = 1ll * ret * x % MOD; 14 x = 1ll * x * x % MOD; y >>= 1; 15 } 16 return ret; 17 } 18 inline int CC(int x, int y) { 19 return 1ll * fac[x] * inv[y] % MOD * inv[x - y] % MOD; 20 } 21 inline void init() { 22 fac[0] = fac[1] = 1; 23 inv[0] = inv[1] = 1; 24 for(int i = 2; i <= 1000; ++ i) { 25 fac[i] = 1ll * fac[i - 1] * i % MOD; 26 inv[i] = 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD; 27 } 28 for(int i = 1; i <= 1000; ++ i) { 29 inv[i] = 1ll * inv[i - 1] * inv[i] % MOD; 30 } 31 for(int i = 0; i <= lim; ++ i) { 32 f[i] = CC(n - 3 * i, i); 33 } 34 } 35 inline void NTT(int *a) { 36 for(int i = 0; i < N; ++ i) { 37 if(rev[i] > i) { 38 swap(a[rev[i]], a[i]); 39 } 40 } 41 for(int d = 1, t = (N >> 1); d < N; d <<= 1, t >>= 1) { 42 for(int i = 0; i < N; i += (d << 1)) { 43 for(int j = 0; j < d; ++ j) { 44 int tmp = 1ll * w[t * j] * a[i + j + d] % MOD; 45 a[i + j + d] = (a[i + j] - tmp + MOD) % MOD; 46 a[i + j] = (a[i + j] + tmp) % MOD; 47 } 48 } 49 } 50 } 51 inline int Do(int n, int m) { 52 int la = A - m, lb = B - m, lc = C - m, ld = D - m; 53 la = min(la, n); lb = min(lb, n); 54 lc = min(lc, n); ld = min(ld, n); 55 int tot = max(la + lb + lc + ld, n); 56 N = 1; int L = 0; 57 for(; N <= tot; N <<= 1, ++ L); 58 for(int i = 0; i < N; ++ i) { 59 rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1)); 60 } 61 for(int i = 0; i < N; ++ i) { 62 a[i] = b[i] = c[i] = d[i] = 0; 63 } 64 for(int i = 0; i <= la; ++ i) { 65 a[i] = inv[i]; 66 } 67 for(int i = 0; i <= lb; ++ i) { 68 b[i] = inv[i]; 69 } 70 for(int i = 0; i <= lc; ++ i) { 71 c[i] = inv[i]; 72 } 73 for(int i = 0; i <= ld; ++ i) { 74 d[i] = inv[i]; 75 } 76 w[0] = 1; w[1] = Power(3, (MOD - 1) / N); 77 for(int i = 2; i < N; ++ i) { 78 w[i] = 1ll * w[i - 1] * w[1] % MOD; 79 } 80 NTT(a), NTT(b), NTT(c), NTT(d); 81 for(int i = 0; i < N; ++ i) { 82 a[i] = 1ll * a[i] * b[i] % MOD * c[i] % MOD * d[i] % MOD; 83 } 84 w[1] = Power(w[1], MOD - 2); 85 for(int i = 2; i < N; ++ i) { 86 w[i] = 1ll * w[i - 1] * w[1] % MOD; 87 } 88 NTT(a); 89 return 1ll * a[n] * Power(N, MOD - 2) % MOD * fac[n] % MOD; 90 } 91 int main() { 92 scanf("%d%d%d%d%d", &n, &A, &B, &C, &D); 93 lim = min(n / 4, min(A, min(B, min(C, D)))); 94 init(); 95 int res = 0; 96 for(int i = 0; i <= lim; ++ i) { 97 int op = 1; 98 if(i & 1) op = -1; 99 res += 1ll * op * f[i] * Do(n - 4 * i, i) % MOD; 100 if(res >= MOD) res -= MOD; 101 if(res < 0) res += MOD; 102 } 103 printf("%d\n", res); 104 }
来源:https://www.cnblogs.com/iamqzh233/p/11431853.html