CCPC-Wannafly Winter Camp Day3 (Div2, onsite)
Replay
Dup4:
- 没想清楚就动手写? 写了两百行发现没用?想的还是不够仔细啊。
- 要有莽一莽的精神
X:
- 感觉今天没啥输出啊, 就推了个公式?抄了个板子, 然后就一直自闭A。
- 语文差,题目没理解,导致写了接近三小时的A吧, 最后看了一眼群, 发现自己理解错了。
- 以及感觉自己最近不会交流,有点毒瘤了。
A:二十四点*
Solved.
思路:
Div2 暴力跑?
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 int n; 6 double arr[20]; 7 8 int main() 9 { 10 while(~scanf("%d", &n)) 11 { 12 for(int i = 1; i <= n; ++i) scanf("%lf", arr + i); 13 else if(n == 6) puts("32"); 14 else if(n == 10) puts("891"); 15 16 } 17 return 0; 18 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const ll MOD = 998244353; 6 const double eps = 1e-8; 7 8 int n, ans; 9 double arr[12]; 10 double brr[12]; 11 12 int DFS(int len) 13 { 14 if (len == 1) 15 { 16 if (fabs(brr[1] - 24.0) < eps) return 1; 17 else return 0; 18 } 19 20 for (int i = 1; i <= len; ++i) 21 { 22 for (int j = i + 1; j <= len; ++j) 23 { 24 double a = brr[i], b = brr[j], c = brr[len]; 25 brr[j] = c; 26 double tmp = a + b; 27 brr[i] = tmp; 28 if (fabs(tmp - 24.0) < eps) return 1; 29 if (DFS(len - 1)) return 1; 30 31 tmp = a - b; 32 brr[i] = tmp; 33 if (fabs(tmp - 24.0) < eps) return 1; 34 if (DFS(len - 1)) return 1; 35 36 tmp = b - a; 37 brr[i] = tmp; 38 if (fabs(tmp - 24.0) < eps) return 1; 39 if (DFS(len - 1)) return 1; 40 41 tmp = a * b; 42 brr[i] = tmp; 43 if (fabs(tmp - 24.0) < eps) return 1; 44 if (DFS(len - 1)) return 1; 45 46 tmp = a / b; 47 brr[i] = tmp; 48 if (fabs(tmp - 24.0) < eps) return 1; 49 if (DFS(len - 1)) return 1; 50 51 tmp = b / a; 52 brr[i] = tmp; 53 if (fabs(tmp - 24.0) < eps) return 1; 54 if (DFS(len - 1)) return 1; 55 56 57 brr[i] = a, brr[j] = b, brr[len] = c; 58 } 59 } 60 61 return 0; 62 } 63 64 void RUN() 65 { 66 while (~scanf("%d", &n)) 67 { 68 for (int i = 1; i <= n; ++i) scanf("%lf", arr + i); 69 ans = 0; 70 for (int i = 0; i < (1 << n); ++i) 71 { 72 int tot = 0; 73 for (int j = 0; j < n; ++j) 74 { 75 if (i & (1 << j)) 76 { 77 brr[++tot] = arr[j + 1]; 78 } 79 } 80 int tmp = DFS(tot); 81 ans += tmp; 82 } 83 printf("%d\n", ans); 84 } 85 } 86 87 int main() 88 { 89 RUN(); 90 return 0; 91 }
D:精简改良
Upsolved.
生成树状压DP
$dp[S][u] 表示点集为S, 根节点为u的最大贡献$
$dp[S][u] = max(dp[T][v] + dp[S - T][u] + dist[u][v] \cdot |T| \cdot |n - T|)$
$T表示点集为T 并且根节点为v的子树,并且T是S的子集$
$要注意有些非法的状态是不能够参与转移的$
$时间复杂度O(3^n \cdot n^2)$
$对于每一个集合枚举其子集 相当于 \sum\limits_{0}^{n} C_n^i \cdot 2^i = (1 + 2)^n = 3^n$
但是这里过不去 要卡点常 喵喵喵? 预处理了一下子集中有哪些点
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define INFLL 0x3f3f3f3f3f3f3f3f 6 #define N 110 7 int n, m; 8 int dist[20][20]; 9 ll f[1 << 15][15]; 10 11 vector <int> vec[1 << 15]; 12 int sze[1 << 15]; 13 14 int main() 15 { 16 while (scanf("%d%d", &n, &m) != EOF) 17 { 18 memset(dist, -1, sizeof dist); 19 memset(f, -1, sizeof f); 20 for (int S = 0, len = (1 << n); S < len; ++S) 21 { 22 for (int i = 0; i < n; ++i) if ((S >> i) & 1) 23 vec[S].push_back(i + 1); 24 sze[S] = vec[S].size(); 25 if (sze[S] == 1) 26 f[S][*vec[S].begin()] = 0; 27 } 28 for (int i = 1, u, v, w; i <= m; ++i) 29 { 30 scanf("%d%d%d", &u, &v, &w); 31 dist[u][v] = dist[v][u] = w; 32 } 33 ll res = 0; 34 for (int S = 1, len = (1 << n); S < len; ++S) 35 { 36 for (auto u : vec[S]) 37 { 38 for (int T = (S - 1) & S; T != 0; T = (T - 1) & S) // 枚举子集 39 { 40 for (auto v : vec[T]) 41 { 42 if (dist[u][v] == -1 || f[T][v] == -1 || f[S - T][u] == -1) continue; 43 f[S][u] = max(f[S][u], f[T][v] + f[S - T][u] + 1ll * dist[u][v] * sze[T] * (n - sze[T])); 44 } 45 } 46 if (S == (1 << n) - 1)res = max(res, f[S][u]); 47 } 48 } 49 printf("%lld\n", res); 50 } 51 return 0; 52 }
E:最大独立集
Upsolved.
树上独立集的贪心做法:
每次取出叶子节点加入答案,然后将其父节点扔掉,循环操作,直至没有节点
那么考虑这一道题的操作,对于一棵树$T(k_i)来说$
它的每一个节点下面都连着一棵独立的树
那么这些独立的树都可以分别贪心求最大独立集
再考虑这些独立的树做完之后剩下的$T(k_i)$
如果这些独立的树的最大独立集需要取到其根节点,那么$T(k_i)中所有节点都不能取$
否则$T(k_i)的贡献就是以k_i为根的最大独立集$
这时候需要预处理出以$x \in [1, n] 为根的最大独立集中是否需要用到x$
树形dp
第一次dp ,$f[i] 表示只考虑i的子树当前点 取不取$
显然,对于所有叶子节点都是取的 ,$f[i] = 1$
那么对于非叶子节点 $f[i] = 1 当且仅当其所有儿子的f[i] = 0$
第二次dp $g[i] 表示不考虑i的子树当前点取不取$
显然,$g[1] = 1 表示根节点不考虑其子树是取的$
再考虑其他点$u,如果u是不取的,当且仅当 g[fa[u]] = 1 并且 其父亲除它$
$以外的儿子中的f[i]都是不取的$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 const ll MOD = (ll)998244353; 7 int n, m; ll base; 8 vector <int> G[N]; 9 10 int fa[N], cnt[N], need[N]; 11 // 0 get > 0 not get for cnt 12 void DFS(int u) 13 { 14 cnt[u] = 0; 15 for (auto v : G[u]) if (v != fa[u]) 16 { 17 fa[v] = u; 18 DFS(v); 19 if (cnt[v] == 0) ++cnt[u]; 20 } 21 if (cnt[u] == 0) ++base; 22 } 23 24 25 // need 0 get 1 not get 26 void DFS2(int u) 27 { 28 if (u != 1) 29 { 30 if (need[fa[u]] == 0 && cnt[fa[u]] - (cnt[u] == 0) == 0) need[u] = 1; 31 else need[u] = 0; 32 } 33 for (auto v : G[u]) if (v != fa[u]) 34 DFS2(v); 35 } 36 37 int main() 38 { 39 while (scanf("%d%d", &n, &m) != EOF) 40 { 41 base = 0; 42 for (int i = 1; i <= n; ++i) G[i].clear(); 43 for (int i = 1, u, v; i < n; ++i) 44 { 45 scanf("%d%d", &u, &v); 46 G[u].push_back(v); 47 G[v].push_back(u); 48 } 49 DFS(1); 50 need[1] = 0; 51 DFS2(1); 52 for (int i = 1; i <= n; ++i) 53 { 54 if (cnt[i] == 0 && need[i] == 0) need[i] = 1; 55 else need[i] = 0; 56 } 57 ll res = base; 58 int vis = need[1]; 59 for (int i = 1, x; i <= m; ++i) 60 { 61 scanf("%d", &x); 62 printf("%lld\n", res); 63 res = (res * n) % MOD; 64 if (vis == 0) res = (res + base) % MOD, vis = need[x]; 65 else vis = 0; 66 } 67 printf("%lld\n", res); 68 } 69 return 0; 70 }
F:小清新数论*
Solved.
思路:
$ans = \sum\limits_{d = 1}^n \cdot \mu(d) \sum\limits_{i = 1}^{\frac{n}{d}} \sum\limits_{j = 1}^{\frac{n}{d}} [gcd(i, j) == 1]$
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const ll MOD = 998244353; 8 const int maxn = 1e7 + 10; 9 10 bool check[maxn]; 11 int prime[maxn]; 12 int mu[maxn]; 13 ll sum[maxn]; 14 15 void Moblus() 16 { 17 mu[1] = 1; 18 int tot = 0; 19 for(int i = 2; i < maxn; ++i) 20 { 21 if(!check[i]) 22 { 23 prime[tot++] = i; 24 mu[i] = -1; 25 } 26 for(int j = 0; j < tot; ++j) 27 { 28 if(i * prime[j] > maxn) break; 29 check[i * prime[j]] = true; 30 if(i % prime[j] == 0) 31 { 32 mu[i * prime[j]] = 0; 33 break; 34 } 35 else 36 { 37 mu[i * prime[j]] = -mu[i]; 38 } 39 } 40 } 41 for(int i = 1; i < maxn; ++i) 42 { 43 sum[i] = (sum[i - 1] + mu[i]) % MOD; 44 } 45 } 46 47 ll solve(int n, int m) 48 { 49 ll ans = 0; 50 if(n > m) swap(n, m); 51 for(int i = 1, la = 0; i <= n; i = la + 1) 52 { 53 la = min(n / (n / i), m / (m / i)); 54 ans += (sum[la] - sum[i - 1]) * (n / i) * (n / i); 55 } 56 return ans; 57 } 58 59 int n; 60 61 int main() 62 { 63 Moblus(); 64 while(~scanf("%d", &n)) 65 { 66 ll ans = 0; 67 for(int i = 1; i <= n; ++i) 68 { 69 ans = (ans + mu[i] * solve(n / i, n / i) % MOD + MOD) % MOD; 70 } 71 printf("%lld\n", ans); 72 } 73 return 0; 74 }
G:排列
Solved.
签到。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 int n, q[N], p[N], vis[N]; 6 7 int main() 8 { 9 while (scanf("%d", &n) != EOF) 10 { 11 memset(vis, 0, sizeof vis); 12 memset(p, 0, sizeof p); 13 for (int i = 1; i <= n; ++i) scanf("%d", q + i); 14 int j = 1; 15 for (int i = 1; i <= n; ++i) 16 { 17 if (i == 1 || q[i] < q[i - 1]) 18 { 19 vis[j] = 1; 20 p[q[i]] = j; 21 ++j; 22 } 23 } 24 for (int i = 1; i <= n; ++i) if (!p[i]) 25 p[i] = j++; 26 for (int i = 1; i <= n; ++i) printf("%d%c", p[i], " \n"[i == n]); 27 } 28 return 0; 29 }
H:涂鸦*
Solved.
思路:
对于某一个点来说,它被染成黑点的概率是$\frac{(x - l + 1) \cdot (r - x + 1) \cdot 2}{(r - l + 1) \cdot (r - l + 2)}$
然后二维BIT维护矩形,最后将所有未被矩形覆盖的期望加起来即可
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 1010 6 const ll p = (ll)998244353; 7 const ll MOD = (ll)998244353; 8 int n, m, q; 9 ll inv[N]; 10 int ans[N][N]; 11 12 namespace BIT 13 { 14 int a[N][N]; 15 void init() { memset(a, 0, sizeof a); } 16 void update(int x, int y, int v) 17 { 18 for (int i = x; i > 0; i -= i & -i) 19 for (int j = y; j > 0; j -= j & -j) 20 a[i][j] += v; 21 } 22 int query(int x, int y) 23 { 24 int res = 0; 25 for (int i = x; i < N; i += i & -i) 26 for (int j = y; j < N; j += j & -j) 27 res += a[i][j]; 28 return res; 29 } 30 void update(int x1, int y1, int x2, int y2) 31 { 32 update(x1 - 1, y1 - 1, 1); 33 update(x1 - 1, y2, -1); 34 update(x2, y1 - 1, -1); 35 update(x2, y2, 1); 36 } 37 } 38 39 int main() 40 { 41 inv[1] = 1; 42 for (int i = 2; i < N; ++i) inv[i] = inv[p % i] * (p - p / i) % p; 43 while (scanf("%d%d%d", &n, &m, &q) != EOF) 44 { 45 memset(ans, 0, sizeof ans); BIT::init(); 46 for (int i = 1, l, r; i <= n; ++i) 47 { 48 scanf("%d%d", &l, &r); 49 for (int j = l; j <= r; ++j) 50 ans[i][j] = 2ll * (j - l + 1) %p * (r - j + 1) %p * inv[r - l + 1] % p * inv[r - l + 2] % p; 51 //cout << ans[i][l] * (r - l + 1) * (r - l + 2) / 2 % p << endl; 52 } 53 for (int qq = 1; qq <= q; ++qq) 54 { 55 int x1, x2, y1, y2; 56 scanf("%d%d%d%d", &x1, &y1, &x2, &y2); 57 BIT::update(x1, y1, x2, y2); 58 } 59 ll res = 0; 60 for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) if (!BIT::query(i, j)) 61 { 62 //cout << i << " " << j << endl; 63 res = (res + ans[i][j]) % p; 64 } 65 printf("%lld\n", res); 66 } 67 return 0; 68 }
I:石头剪刀布
Solved.
思路:
考虑有$x之前要被a个人挑战,要挑战b个人$
那么$此时对于x满足的方案数是3^n \cdot \frac{1}{3}^b \cdot \frac{2}{3}^a$
然后考虑怎么维护$a和b$
我们发现对于1操作
我们将$y 接在 x 下面的话,这样最后会构成一棵树$
那么$b就是祖宗的个数$
再考虑$a是什么,我们发现其实就是 它的所有祖先当中 所有比它后加入的儿子个数 以及它所有的儿子个数$
然后再发现一棵子树对其他点的影响,发现受影响的点就是它的父亲中比它先来的儿子中所对应的所有子树
这一段在$DFS序中是连续的,线段树维护即可$
刚开始错误思路,写了个树剖,喵喵喵?
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 const ll MOD = (ll)998244353; 7 int n, m, fa[N]; 8 vector <int> G[N]; 9 struct qnode 10 { 11 int op, x, y; 12 void scan() 13 { 14 scanf("%d%d", &op, &x); ++x; 15 if (op == 1) 16 { 17 scanf("%d", &y); ++y; 18 fa[y] = x; 19 G[x].push_back(y); 20 } 21 } 22 }q[N]; 23 24 int deep[N], sze[N], top[N], son[N], lp[N], rp[N], fp[N], cnt; 25 void DFS(int u) 26 { 27 sze[u] = 1; 28 for (auto v : G[u]) if (v != fa[u]) 29 { 30 deep[v] = deep[u] + 1; 31 DFS(v); 32 sze[u] += sze[v]; 33 if (!son[u] && sze[v] > sze[son[u]]) son[u] = v; 34 } 35 } 36 37 void getpos(int u, int sp) 38 { 39 top[u] = sp; 40 lp[u] = ++cnt; 41 fp[cnt] = u; 42 if (!son[u]) 43 { 44 rp[u] = cnt; 45 return; 46 } 47 getpos(son[u], sp); 48 for (auto v : G[u]) if (v != fa[u] && v != son[u]) 49 getpos(v, v); 50 rp[u] = cnt; 51 } 52 53 namespace SEG 54 { 55 struct node 56 { 57 int v[2], lazy[2]; 58 node() {} 59 void init() 60 { 61 memset(v, 0, sizeof v); 62 memset(lazy, 0, sizeof lazy); 63 } 64 node operator + (const node &other) const 65 { 66 node res; res.init(); 67 for (int i = 0; i < 2; ++i) res.v[i] = v[i] + other.v[i]; 68 return res; 69 } 70 }a[N << 2], res; 71 void build(int id, int l, int r) 72 { 73 a[id].init(); 74 if (l == r) 75 { 76 a[id].v[0] = deep[fp[l]]; 77 return; 78 } 79 int mid = (l + r) >> 1; 80 build(id << 1, l, mid); 81 build(id << 1 | 1, mid + 1, r); 82 } 83 void pushdown(int id) 84 { 85 for (int i = 0; i < 2; ++i) if (a[id].lazy[i]) 86 { 87 int lazy = a[id].lazy[i]; 88 a[id].lazy[i] = 0; 89 a[id << 1].v[i] += lazy; 90 a[id << 1 | 1].v[i] += lazy; 91 a[id << 1].lazy[i] += lazy; 92 a[id << 1 | 1].lazy[i] += lazy; 93 } 94 } 95 void update(int id, int l, int r, int ql, int qr, int v, int vis) 96 { 97 if (l >= ql && r <= qr) 98 { 99 a[id].v[vis] += v; 100 a[id].lazy[vis] += v; 101 return; 102 } 103 int mid = (l + r) >> 1; 104 pushdown(id); 105 if (ql <= mid) update(id << 1, l, mid, ql, qr, v, vis); 106 if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr, v, vis); 107 } 108 void query(int id, int l, int r, int pos) 109 { 110 if (l == r) 111 { 112 res = a[id]; 113 return; 114 } 115 int mid = (l + r) >> 1; 116 pushdown(id); 117 if (pos <= mid) query(id << 1, l, mid, pos); 118 else query(id << 1 | 1, mid + 1,r , pos); 119 } 120 } 121 122 ll qmod(ll base, ll n) 123 { 124 ll res = 1; 125 while (n) 126 { 127 if (n & 1) res = (res * base) % MOD; 128 base = base * base % MOD; 129 n >>= 1; 130 } 131 return res; 132 } 133 ll ans[N]; 134 135 int main() 136 { 137 while (scanf("%d%d", &n, &m) != EOF) 138 { 139 ++n; 140 for (int i = 1; i <= n; ++i) G[i].clear(); 141 memset(fa, 0, sizeof fa); 142 memset(son, 0, sizeof son); cnt = 0; 143 for (int i = 1; i <= m; ++i) q[i].scan(); 144 for (int i = 2; i <= n; ++i) if (!fa[i]) 145 { 146 fa[i] = 1; 147 G[1].push_back(i); 148 } 149 deep[1] = -1; 150 DFS(1); getpos(1, 1); 151 SEG::build(1, 1, n); 152 for (int i = 2; i <= n; ++i) if (fa[i] != 1) 153 SEG::update(1, 1, n, lp[fa[i]], lp[i] - 1, 1, 1); 154 for (int i = m; i >= 1; --i) 155 { 156 if (q[i].op == 2) 157 { 158 SEG::res.init(); 159 SEG::query(1, 1, n, lp[q[i].x]); 160 int x = SEG::res.v[0], y = SEG::res.v[1]; 161 ans[i] = qmod(3, n - x - y - 1) * qmod(2, y) % MOD; 162 } 163 else 164 { 165 SEG::res.init(); 166 SEG::query(1, 1, n, lp[q[i].y]); 167 int x = SEG::res.v[0]; 168 SEG::update(1, 1, n, lp[q[i].y], rp[q[i].y], -x, 0); 169 SEG::update(1, 1, n, lp[q[i].x], lp[q[i].y] - 1, -1, 1); 170 } 171 } 172 for (int i = 1; i <= m; ++i) if (q[i].op == 2) printf("%lld\n", ans[i]); 173 } 174 return 0; 175 }
并查集
按秩合并(为什么路径压缩不可以呢, 据说是因为破坏了树结构?)
每次对于$x$, $y$两个集合
当把$y$集合合并到$x$集合中时:
- 使得$y$上的总比赛场次增量减去$x$原本的总比赛场次增量, 同时使得$x$总比赛场次增量增加
- 使得$x$总主场场次增量增加, 同时使得$y$上的总主场场次增量减去$x$现在的总主场场次增量
当把$x$集合合并到$y$集合中时:
- 使得$x$上的总比赛场次增量减去$y$原本的总比赛场次增量, 同时使得$y$总比赛场次增量增加
- 使得$x$上的总主场场次增量减去$y$原本的总主场场次增量, 同时是的$x$总主场场次增量增加
在寻找根节点时, 统计路径上的总主场场次增量$a$以及总主场场次增量$b$
那么此时方案数为$3^n\cdot2^a\cdot(\frac{1}{3})^b$
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const ll MOD = 998244353; 8 const int maxn = 2e5 + 10; 9 10 ll qpow(ll x, ll n) 11 { 12 assert(n >= 0); 13 ll res = 1; 14 while (n) 15 { 16 if (n & 1) res = res * x % MOD; 17 x = x * x % MOD; 18 n >>= 1; 19 } 20 return res; 21 } 22 23 struct node { 24 int father; 25 int dw; 26 int dv; 27 node() {} 28 node(int father, int dw, int dv) :father(father), dw(dw), dv(dv) {} 29 }; 30 31 int n, m; 32 int fa[maxn]; 33 int rk[maxn]; 34 int w[maxn], v[maxn];//sum 主 35 int dw[maxn], dv[maxn]; 36 int tot; 37 38 void Init() 39 { 40 tot = n + 1; 41 for (int i = 1; i < maxn; ++i) fa[i] = i, w[i] = 0, v[i] = 0, rk[i] = 0; 42 } 43 44 node find(int x) 45 { 46 if (x == fa[x]) return node(fa[x], dw[x], dv[x]); 47 node temp = find(fa[x]); 48 return node(temp.father, temp.dw + dw[x], temp.dv + dv[x]); 49 } 50 51 void Mix(int x, int y) 52 { 53 x = find(x).father, y = find(y).father; 54 if (rk[x] >= rk[y]) 55 { 56 rk[x] = max(rk[x], rk[y] + 1); 57 fa[y] = x; 58 59 dw[y] -= dw[x]; 60 dw[x]++; 61 dv[x]++; 62 dv[y] -= dv[x]; 63 } 64 else 65 { 66 rk[y] = max(rk[y], rk[x] + 1); 67 fa[x] = y; 68 69 dw[x] -= dw[y]; 70 dw[y]++; 71 dv[x]++; 72 dv[x] -= dv[y]; 73 } 74 } 75 76 void RUN() 77 { 78 while (~scanf("%d %d", &n, &m)) 79 { 80 Init(); 81 for (int q = 1, op, x, y; q <= m; ++q) 82 { 83 scanf("%d", &op); 84 if (op == 1) 85 { 86 scanf("%d %d", &x, &y); 87 Mix(x, y); 88 } 89 else if (op == 2) 90 { 91 scanf("%d", &x); 92 node tmp = find(x); 93 ll ans = qpow(3, n) * qpow((qpow(3, tmp.dw)), MOD - 2) % MOD * qpow(2, tmp.dv) % MOD; 94 printf("%lld\n", ans); 95 } 96 } 97 } 98 } 99 100 int main() 101 { 102 #ifdef LOCAL_JUDGE 103 freopen("Text.txt", "r", stdin); 104 #endif // LOCAL_JUDGE 105 106 RUN(); 107 108 #ifdef LOCAL_JUDGE 109 fclose(stdin); 110 #endif // LOCAL_JUDGEf 111 112 return 0; 113 }