SDOI泛做

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

转载于:https://www.cnblogs.com/iamqzh233/p/11431853.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值