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
