2024.04 别急记录
1. 餐巾计划问题
建图跑费用流即可:
; ; ; ; ; ; ;
解释一下 234:
考虑把餐巾作为流,费用作为费用。则我们需要保证最大流为
点击查看代码
//P1251 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 4e3 + 10, M = 4e4 + 10; const ll inf = 1e10; int n, r[N], p, u, f, v, s; ll mc, ln[M], cs[M], ds[N]; int hd[N], eg[M], nx[M], tot = 1, nw[N], vs[N]; void adg(int u, int v, ll w, ll c){ eg[++tot] = v; ln[tot] = w; cs[tot] = c; nx[tot] = hd[u]; hd[u] = tot; } void add(int u, int v, ll w, ll c){ // printf("%d %d %lld %lld\n", u, v, w, c); adg(u, v, w, c); adg(v, u, 0, -c); } bool spfa(int s, int t){ queue<int> q; memset(ds, 0x3f, sizeof(ds)); memcpy(nw, hd, sizeof(hd)); q.push(s); ds[s] = 0; vs[s] = 1; while(!q.empty()){ int x = q.front(); q.pop(); vs[x] = 0; for(int i = hd[x]; i; i = nx[i]){ int y = eg[i]; ll z = ln[i], c = cs[i]; if(z && ds[y] > ds[x] + c){ ds[y] = ds[x] + c; if(!vs[y]){ vs[y] = 1; q.push(y); } } } } return ds[t] != 0x3f3f3f3f3f3f3f3f; } ll dfs(int x, int t, ll fl){ if(x == t){ return fl; } ll rs = fl; vs[x] = 1; for(int i = nw[x]; i && rs; i = nx[i]){ int y = eg[i]; ll z = ln[i], c = cs[i]; nw[x] = i; if(!vs[y] && z && ds[y] == ds[x] + c){ ll k = dfs(y, t, min(rs, z)); if(!k){ ds[y] = 0; } ln[i] -= k; ln[i^1] += k; rs -= k; mc += k * c; } } vs[x] = 0; return fl - rs; } void solve(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &r[i]); } scanf("%d%d%d%d%d", &p, &u, &f, &v, &s); int st = n + n + 1, ed = n + n + 2; for(int i = 1; i <= n; ++ i){ add(i, i+n, r[i], 0); add(st, i+n, r[i], 0); add(i, ed, r[i], 0); if(i < n){ add(i, i+1, inf, 0); } if(i + v <= n){ add(i+n, i+v, inf, s); } if(i + u <= n){ add(i+n, i+u, inf, f); } } add(st, 1, inf, p); while(spfa(st, ed)){ while(dfs(st, ed, inf*2)); } printf("%lld\n", mc); }
2. 国家集训队 - happiness
考虑答案=总和-最小割。
对于点
对于额外贡献
点击查看代码
//P1646 #include <bits/stdc++.h> using namespace std; const int N = 5e4 + 10, inf = 100000, M = 3e5 + 10; int n, m, hd[N], eg[M], ln[M], nx[M], tot = 1, now[N], dep[N]; void adg(int u, int v, int w){ eg[++tot] = v; ln[tot] = w; nx[tot] = hd[u]; hd[u] = tot; } void add(int u, int v, int w){ adg(u, v, w); adg(v, u, 0); } bool bfs(int s, int t){ queue<int> q; q.push(s); memcpy(now, hd, sizeof(hd)); memset(dep, 0, sizeof(dep)); dep[s] = 1; while(!q.empty()){ int x = q.front(); q.pop(); for(int i = hd[x]; i; i = nx[i]){ int y = eg[i], z = ln[i]; if(z && !dep[y]){ dep[y] = dep[x] + 1; q.push(y); if(y == t){ return 1; } } } } return 0; } int dfs(int x, int t, int fl){ if(x == t){ return fl; } else { int rs = fl; for(int i = now[x]; i && rs; i = nx[i]){ int y = eg[i], z = ln[i]; now[x] = i; if(z && dep[y] == dep[x] + 1){ int k = dfs(y, t, min(rs, z)); if(!k){ dep[y] = 0; } ln[i] -= k; ln[i^1] += k; rs -= k; } } return fl - rs; } } int main(){ #define cg(x, y) ((x - 1) * m + y) scanf("%d%d", &n, &m); int sm = 0; int st = cg(n, m) + 1, ed = cg(n, m) + 2, a, cnt = cg(n, m) + 2; for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ scanf("%d", &a); sm += a; add(st, cg(i, j), a); } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ scanf("%d", &a); sm += a; add(cg(i, j), ed, a); } } for(int i = 1; i < n; ++ i){ for(int j = 1; j <= m; ++ j){ scanf("%d", &a); sm += a; ++ cnt; add(st, cnt, a); add(cnt, cg(i, j), inf); add(cnt, cg(i+1, j), inf); } } for(int i = 1; i < n; ++ i){ for(int j = 1; j <= m; ++ j){ scanf("%d", &a); sm += a; ++ cnt; add(cnt, ed, a); add(cg(i, j), cnt, inf); add(cg(i+1, j), cnt, inf); } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j < m; ++ j){ scanf("%d", &a); sm += a; ++ cnt; add(st, cnt, a); add(cnt, cg(i, j), inf); add(cnt, cg(i, j+1), inf); } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j < m; ++ j){ scanf("%d", &a); sm += a; ++ cnt; add(cnt, ed, a); add(cg(i, j), cnt, inf); add(cg(i, j+1), cnt, inf); } } int mf = 0, tmp; while(bfs(st, ed)){ while(tmp = dfs(st, ed, inf)) mf += tmp; } printf("%d\n", sm - mf); return 0; }
3. qoj1427 - Flip
考虑
然后
两个部分分别都可以根号做。
点击查看代码
//qoj1427 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 998244353; const int N = 2e5 + 10; int n, m, c, a[N]; ll fac[N], inv[N], pw[N], ivy, ans[N]; vector<pair<int, int> > g[N], h[N]; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } ll C(int x, int y){ if(x < y) return 0; return fac[x] * inv[y] % P * inv[x-y] % P; } int main(){ fac[0] = pw[0] = 1; for(int i = 1; i < N; ++ i){ fac[i] = fac[i-1] * i % P; pw[i] = pw[i-1] * 2 % P; } inv[N-1] = qp(fac[N-1], P-2); for(int i = N - 2; i >= 0; -- i){ inv[i] = inv[i+1] * (i+1) % P; } scanf("%d%d", &n, &m); ivy = qp(pw[n+n], P-2); for(int cs = 1; cs <= m; ++ cs){ scanf("%d", &c); for(int i = 1; i <= c; ++ i){ scanf("%d", &a[i]); } if(a[c] < n + n){ ans[cs] = (ans[cs] + C(a[c]-c, n-c) * pw[n+n-a[c]]) % P; } g[c].push_back(make_pair(max(n, a[c]+1), cs)); int ls = n - 1; if(c > 450){ for(int i = 1; i <= c; ++ i){ if(a[i] > ls){ for(int k = ls+1; k < a[i]; ++ k){ ans[cs] = (ans[cs] + C(k-i, n-1) * pw[n+n-k]) % P; } ls = a[i]; } } h[c+1].push_back(make_pair(ls+1, cs)); h[c+1].push_back(make_pair(n+n, -cs)); } else { for(int i = 1; i <= c; ++ i){ if(a[i] > ls){ h[i].push_back(make_pair(ls+1, cs)); h[i].push_back(make_pair(a[i], -cs)); ls = a[i]; } } h[c+1].push_back(make_pair(ls+1, cs)); h[c+1].push_back(make_pair(n+n, -cs)); } } for(int i = 1; i <= n; ++ i){ if(g[i].size()){ sort(g[i].begin(), g[i].end()); reverse(g[i].begin(), g[i].end()); int nw = n + n; ll rs = 0; for(auto j : g[i]){ while(nw > j.first){ -- nw; rs = (rs + C(nw-1-i, n-1-i) * pw[n+n-nw]) % P; } ans[j.second] = (ans[j.second] + rs) % P; } } } for(int i = 1; i <= n + 1; ++ i){ if(h[i].size()){ sort(h[i].begin(), h[i].end()); reverse(h[i].begin(), h[i].end()); int nw = n + n; ll rs = 0; for(auto j : h[i]){ while(nw > j.first){ -- nw; rs = (rs + C(nw-i, n-1) * pw[n+n-nw]) % P; } if(j.second > 0){ ans[j.second] = (ans[j.second] + rs) % P; } else { ans[-j.second] = (ans[-j.second] - rs + P) % P; } } } } for(int i = 1; i <= m; ++ i){ printf("%lld\n", 2 * ans[i] * ivy % P); } return 0; }
4. USACO 2024 OPEN - Cowreography G
考虑第一个串中 '1' 的位置为
点击查看代码
//P10280 #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int n, k; char s[N], t[N]; long long ans; set<pair<int, int> > st[2]; int main(){ scanf("%d%d", &n, &k); scanf("%s%s", s+1, t+1); for(int i = 1; i <= n; ++ i){ if(s[i] != t[i]){ int x = s[i] - '0'; if(st[!x].empty()){ st[x].insert(make_pair(i%k, i)); } else { auto it = st[!x].lower_bound(make_pair(i % k, 0)); if(it == st[!x].end()){ it = st[!x].begin(); } ans += (i - (*it).second - 1) / k + 1; st[!x].erase(it); } } } printf("%lld\n", ans); return 0; }
5. CCO2019 - Sirtet
首先 dfs 出所有连通块。对于一个连通块
对于两个连通块
由于建
不卡 spfa 啊。
点击查看代码
//P5532 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e6 + 10; const int dx[4] = {0, -1, 1, 0}, dy[4] = {-1, 0, 0, 1}; int plv[N], pcl[N], vs[N], ds[N], n, m, cs; vector<pair<int, int> > g[N]; char pl[N], ans[N]; void dfs(int x, int y, int l, int c){ char (&s)[n+3][m+3] = decltype(s)(pl); int (&cl)[n+3][m+3] = decltype(cl)(pcl); int (&lv)[n+3][m+3] = decltype(lv)(plv); lv[x][y] = l; cl[x][y] = c; for(int i = 0; i < 4; ++ i){ int xx = x + dx[i], yy = y + dy[i]; if(s[xx][yy] == '#' && !lv[xx][yy]){ dfs(xx, yy, l-dx[i], c); } } } void spfa(int st){ queue<int> q; memset(vs, 0, sizeof(vs)); memset(ds, 0x3f, sizeof(ds)); vs[st] = 1, ds[st] = 0, q.push(st); while(!q.empty()){ int x = q.front(); q.pop(); vs[x] = 0; for(auto i : g[x]){ if(ds[i.first] > ds[x] + i.second){ ds[i.first] = ds[x] + i.second; if(!vs[i.first]){ q.push(i.first); vs[i.first] = 1; } } } } } int main(){ scanf("%d%d", &n, &m); char (&s)[n+3][m+3] = decltype(s)(pl); char (&t)[n+3][m+3] = decltype(t)(ans); int (&cl)[n+3][m+3] = decltype(cl)(pcl); int (&lv)[n+3][m+3] = decltype(lv)(plv); for(int i = 1; i <= n; ++ i){ scanf("%s", s[i] + 1); } for(int i = n; i >= 1; -- i){ for(int j = 1; j <= m; ++ j){ t[i][j] = '.'; if(!lv[i][j] && s[i][j] == '#'){ dfs(i, j, 1, ++ cs); } } } for(int j = 1; j <= m; ++ j){ int ls = 0; for(int i = n; i >= 1; -- i){ if(s[i][j] != '#'){ continue; } if(ls && cl[i][j] != cl[ls][j]){ g[cl[ls][j]].emplace_back(cl[i][j], lv[i][j]-lv[ls][j]-1); } ls = i; } } for(int i = 1; i <= cs; ++ i){ g[cs+1].emplace_back(i, 0); } spfa(cs + 1); for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ if(s[i][j] == '#'){ t[ds[cl[i][j]]+n-lv[i][j]+1][j] = '#'; } } } for(int i = 1; i <= n; ++ i){ printf("%s\n", t[i] + 1); } return 0; }
6. LuoguP3600 - 随机数生成器
水黑逆天
考虑设
点击查看代码
//P3600 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2010; const ll P = 666623333; int n, x, q, mr[N]; ll f[N][N], g[N]; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } ll t[N*4], tag[N*4]; void psd(int p){ t[p<<1] = t[p<<1] * tag[p] % P; t[p<<1|1] = t[p<<1|1] * tag[p] % P; tag[p<<1] = tag[p<<1] * tag[p] % P; tag[p<<1|1] = tag[p<<1|1] * tag[p] % P; tag[p] = 1; } void mul(int p, int l, int r, int ql, int qr, ll v){ if(ql <= l && r <= qr){ t[p] = t[p] * v % P; tag[p] = tag[p] * v % P; } else if(r < ql || qr < l){ return; } else { int mid = l + r >> 1; psd(p); mul(p<<1, l, mid, ql, qr, v); mul(p<<1|1, mid+1, r, ql, qr, v); t[p] = (t[p<<1] + t[p<<1|1]) % P; } } void add(int p, int l, int r, int x, ll v){ if(l == r){ t[p] = (t[p] + v) % P; } else { int mid = l + r >> 1; psd(p); if(x <= mid){ add(p<<1, l, mid, x, v); } else { add(p<<1|1, mid+1, r, x, v); } t[p] = (t[p<<1] + t[p<<1|1]) % P; } } ll qry(int p, int l, int r, int ql, int qr){ if(ql <= l && r <= qr){ return t[p]; } else if(r < ql || qr < l){ return 0; } else { int mid = l + r >> 1; psd(p); return (qry(p<<1, l, mid, ql, qr) + qry(p<<1|1, mid+1, r, ql, qr)) % P; } } int main(){ scanf("%d%d%d", &n, &x, &q); for(int i = 1; i <= q; ++ i){ int l, r; scanf("%d%d", &l, &r); mr[r] = max(mr[r], l); } for(int T = 1; T <= x; ++ T){ mul(1, 0, n, 0, n, 0); add(1, 0, n, 0, 1); for(int i = 1; i <= n; ++ i){ ll val = t[1] * T % P; t[1] = t[1] * (x-T) % P; tag[1] = tag[1] * (x-T) % P; mul(1, 0, n, 0, mr[i]-1, 0); add(1, 0, n, i, val); } g[T] = t[1]; } ll ans = 0; for(int i = n; i >= 1; -- i){ g[i] = (g[i] - g[i-1] + P) % P; ans += g[i] * i % P; ans %= P; } printf("%lld\n", ans * qp(qp(x, n), P-2) % P); return 0; }
7. AGC066A - Adjacent Difference
设
考虑对矩阵黑白染色,相邻位置不同色,有两种末方案:
- 黑色位置取
,白色位置取 ; - 黑色位置取
,白色位置取 ;
容易发现二者的代价和为
点击查看代码
//AT_agc066_a #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 510; int n, d, a[N][N], b[N][N], c[N][N], sb, sc; void solve(){ scanf("%d%d", &n, &d); for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= n; ++ j){ scanf("%d", &a[i][j]); a[i][j] += 1000; if(((i + j) & 1) ^ ((a[i][j] / d) & 1)){ b[i][j] = a[i][j] - a[i][j] % d; c[i][j] = b[i][j] + d; sb += a[i][j] - b[i][j]; sc += c[i][j] - a[i][j]; } else { c[i][j] = a[i][j] - a[i][j] % d; b[i][j] = c[i][j] + d; sc += a[i][j] - c[i][j]; sb += b[i][j] - a[i][j]; } } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= n; ++ j){ printf("%d ", (sb < sc ? b[i][j] : c[i][j]) - 1000); } puts(""); } }
8. AGC066B - Decreasing Digit Sums
考虑对于形如
; ; ; ; 。
假设最高位是
点击查看代码
//AT_agc066_b #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 10010; int s[N], t[N]; int n, len = 1, tot = 1; void solve(){ cin >> n; t[1] = s[1] = 5; for(int i = 2; i <= 160; ++ i){ for(int j = 1; j <= len; ++ j){ t[j] = t[j] * 5; t[j] = t[j] + t[j-1] / 10; t[j-1] %= 10; } while(t[len] >= 10){ t[len+1] = t[len] / 10; t[len] %= 10; ++ len; } for(int j = 1; j <= len; ++ j){ s[++tot] = t[j]; } } for(int i = tot; i >= 1; -- i){ printf("%d", s[i]); } puts(""); }
9. AGC066C - Delete AAB or BAA
一个结论:
若一个串能够被删空,当且仅当它满足以下两个条件,或它能拆分成若干个满足以下两个条件的子串:
A
的数量为B
的两倍。- 首尾中有一个是
B
。
对于本身满足两个条件的串
- 若
,AAB
,BAA
满足条件; - 找到一段长度
的极长A
段,它的两侧各为空或者一个B
,容易发现至少有一个B
不在两侧,删掉这个B
以及相邻的两个A
变成了 的子问题且依旧满足条件 2。
而对于任意一个能够被删空的串,考虑从空串往中间插入,容易发现肯定也满足条件。
所以可以进行 dp。设
设
点击查看代码
//AT_agc066_c #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 5e6 + 10; int T, n, f[N], g[N], h[N]; char s[N]; void solve(){ scanf("%d", &T); while(T--){ scanf("%s", s + 1); n = strlen(s + 1); int sum = N / 2; for(int i = sum - n*2; i <= sum + n*2; ++ i){ g[i] = h[i] = 0x3f3f3f3f; } for(int i = 0; i <= n; ++ i){ if(i){ f[i] = f[i-1] + 1; if(s[i] == 'A'){ ++ sum; f[i] = min(f[i], g[sum]); } else { -- sum; -- sum; f[i] = min(f[i], g[sum]); f[i] = min(f[i], h[sum]); } } if(s[i+1] == 'B'){ g[sum] = min(g[sum], f[i]); } else { h[sum] = min(h[sum], f[i]); } } printf("%d\n", (n - f[n]) / 3); for(int i = 0; i <= n; ++ i){ f[i] = 0; } } }
10. CF526F - Pudding Monsters
考虑从左往右枚举右端点
新增右端点
点击查看代码
//CF526F #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 3e5 + 10; int n, a[N], mx[N], mn[N], tx, tn; ll ans = 0; struct node{ int tag, mn, cnt; } t[N*4]; void psd(int p){ t[p<<1].tag += t[p].tag; t[p<<1].mn += t[p].tag; t[p<<1|1].tag += t[p].tag; t[p<<1|1].mn += t[p].tag; t[p].tag = 0; } void build(int p, int l, int r){ t[p].cnt = r - l + 1; if(l == r){ return; } int mid = l + r >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); } void add(int p, int l, int r, int ql, int qr, int x){ if(qr < l || r < ql){ return; } else if(ql <= l && r <= qr){ t[p].mn += x; t[p].tag += x; } else { int mid = l + r >> 1; psd(p); add(p<<1, l, mid, ql, qr, x); add(p<<1|1, mid+1, r, ql, qr, x); t[p].mn = min(t[p<<1].mn, t[p<<1|1].mn); if(t[p<<1].mn < t[p<<1|1].mn){ t[p].cnt = t[p<<1].cnt; } else if(t[p<<1].mn > t[p<<1|1].mn){ t[p].cnt = t[p<<1|1].cnt; } else { t[p].cnt = t[p<<1].cnt + t[p<<1|1].cnt; } } } pair<int, int> qry(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return make_pair(1e9, 0); } else if(ql <= l && r <= qr){ return make_pair(t[p].mn, t[p].cnt); } else { int mid = l + r >> 1; psd(p); pair<int, int> x = qry(p<<1, l, mid, ql, qr); pair<int, int> y = qry(p<<1|1, mid+1, r, ql, qr); if(x.first < y.first){ return x; } else if(x.first > y.first){ return y; } else { return make_pair(x.first, x.second + y.second); } } } void solve(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ int x, y; scanf("%d%d", &x, &y); a[x] = y; } build(1, 1, n); for(int i = 1; i <= n; ++ i){ add(1, 1, n, i, i, i); while(tx && a[mx[tx]] < a[i]){ add(1, 1, n, mx[tx-1]+1, mx[tx], -a[mx[tx]]); -- tx; } add(1, 1, n, mx[tx]+1, i, a[i]); mx[++tx] = i; while(tn && a[mn[tn]] > a[i]){ add(1, 1, n, mn[tn-1]+1, mn[tn], a[mn[tn]]); -- tn; } add(1, 1, n, mn[tn]+1, i, -a[i]); mn[++tn] = i; ans += qry(1, 1, n, 1, i).second; } printf("%lld\n", ans); }
11. NOI2023 - 贸易
一条路径
点击查看代码
//P9481 //You can get an NOI2023 Ag by solving this problem! //You can get an NOI2023 Au by 509pts. //You can get 5pts by O(3^n) 后面忘了 //509+5=514 //then 进入集训队!!! #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 262150; const ll P = 998244353; int n, m, a[N], dep[N], vis[N]; vector<pair<int, int> > g[N]; vector<int> eg[N], son[N]; ll sum[N], dis[N], ans, fa[N][20]; struct edge{ int u, v, w; } e[N]; int main(){ scanf("%d%d", &n, &m); n = (1 << n) - 1; dep[1] = 1; son[1].push_back(1); for(int i = 2; i <= n; ++ i){ son[i].push_back(i); scanf("%d", &a[i]); dep[i] = dep[i>>1] + 1; for(int j = 1; j <= dep[i]; ++ j){ fa[i][j] = fa[i][j-1] + a[i>>(j-1)]; sum[i>>j] = (sum[i>>j] + fa[i][j]) % P; son[i>>j].push_back(i); } } for(int i = 1; i <= m; ++ i){ int u, v, w; scanf("%d%d%d", &u, &v, &w); e[i] = { u, v, w }; g[u].emplace_back(v, w); int x = v; while(x != u){ eg[x].push_back(i); x >>= 1; } } for(int rt = 1; rt <= n / 2; ++ rt){ priority_queue<pair<ll, int> > q; for(int j : son[rt]){ dis[j] = 1e18; vis[j] = 0; } dis[rt] = 0; q.push(make_pair(-dis[rt], rt)); while(!q.empty()){ int x = q.top().second; q.pop(); if(vis[x] == 1){ continue; } vis[x] = 1; if(x == rt){ for(int i : eg[x]){ int y = e[i].v; ll z = e[i].w; z += fa[x][dep[x] - dep[e[i].u]]; if(dis[y] > dis[x] + z){ dis[y] = dis[x] + z; q.push(make_pair(-dis[y], y)); } } } for(auto i : g[x]){ int y = i.first; ll z = i.second; if(dis[y] > dis[x] + z){ dis[y] = dis[x] + z; q.push(make_pair(-dis[y], y)); } } if((x>>1) >= rt && dis[x>>1] > dis[x] + a[x]){ dis[x>>1] = dis[x] + a[x]; q.push(make_pair(-dis[x>>1], x>>1)); } } ll le = 0, cle = 1; for(int j : son[rt<<1]){ if(dis[j] > 1e16){ continue; } ++ cle; le = (le + dis[j]) % P; } ans = (ans + cle * (sum[rt<<1|1] + a[rt<<1|1] * son[rt<<1|1].size() % P)) % P; ans = (ans + le * (son[rt<<1|1].size() + 1)) % P; cle = 1, le = 0; for(int j : son[rt<<1|1]){ if(dis[j] > 1e16){ continue; } ++ cle; le = (le + dis[j]) % P; } ans = (ans + cle * (sum[rt<<1] + a[rt<<1] * son[rt<<1].size() % P)) % P; ans = (ans + le * (son[rt<<1].size() + 1)) % P; } printf("%lld\n", ans); return 0; }
12. THUPC 2024 初赛 - 多折较差验证
首先预处理
点击查看代码
//P9964 #include <bits/stdc++.h> using namespace std; const int N = 5010; int n, f[N][N][2], ok[N][N], g[N][N][2]; char s[N]; int main(){ scanf("%d%s", &n, s+1); for(int len = 1; len <= n; len += 2){ for(int l = 1; l + len - 1 <= n; ++ l){ int r = l + len - 1; if(l == r){ ok[l][r] = 1; } else { ok[l][r] = (s[l] != s[r]) & ok[l+1][r-1]; } if(ok[l][r]){ g[l][r][0] = g[l][r][1] = (l+r) >> 1; } } } memset(f, 0x3f, sizeof(f)); for(int i = 1; i <= n; ++ i){ for(int j = i; j <= n; ++ j){ if(g[i][j][0] == 0){ g[i][j][0] = g[i][j-1][0]; } } } for(int i = n; i >= 1; -- i){ for(int j = i; j >= 1; -- j){ if(g[j][i][1] == 0){ g[j][i][1] = g[j+1][i][1]; } } } for(int len = 1; len <= n; ++ len){ for(int l = 1; l + len - 1 <= n; ++ l){ int r = l + len - 1; if(l == r){ f[l][r][0] = 1; f[l][r][1] = 0; } else { int x = g[l][r][0], y = g[l][r][1]; if(f[x+1][r][0] + 1 < f[l][r][0]){ f[l][r][0] = f[x+1][r][0] + 1; f[l][r][1] = f[x+1][r][1] + r + l - x - x; } else if(f[x+1][r][0] + 1 == f[l][r][0]){ f[l][r][1] = min(f[l][r][1], f[x+1][r][1] + r + l - x - x); } if(f[l][y-1][0] + 1 < f[l][r][0]){ f[l][r][0] = f[l][y-1][0] + 1; f[l][r][1] = f[l][y-1][1] + y + y - r - l; } else if(f[l][y-1][0] + 1 == f[l][r][0]){ f[l][r][1] = min(f[l][r][1], f[l][y-1][1] + y + y - r - l); } } } } printf("%d %d\n", f[1][n][0], f[1][n][1]); return 0; }
13. AGC035C - Skolem XOR Tree
显然
其他情况如图构造:
点击查看代码
//AT_agc035_c #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n; int main(){ scanf("%d", &n); int p = 1; while(p < n){ p *= 2; } if(p == n){ puts("No"); } else { puts("Yes"); printf("%d %d\n", 1+n, 3); for(int i = 2; i <= n - 1; i += 2){ printf("%d %d\n", i, 1); printf("%d %d\n", i, i+1); printf("%d %d\n", i+n+1, 1); printf("%d %d\n", i+n+1, i+n); } if(n % 2 == 0){ printf("%d %d\n", n, n-2); printf("%d %d\n", n+n, n+(n^(n-2)^1)); } } return 0; }
14. CF1874E - Jellyfish and Hack
首先可以设
设
设
观察到若
接下来可以倒着用拉格朗日插值,已知点值求出表达式。设
枚举
总复杂度
点击查看代码
//CF1874E #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 210; const ll P = 1e9 + 7; ll y[N*N], C[N][N], F[N]; ll fac[N*N], f[N*N], p[N*N], q[N*N]; int n, lim, m; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } void solve(){ cin >> n >> lim; m = n * (n + 1) / 2 + 1; for(int i = 0; i <= n; ++ i){ C[i][0] = C[i][i] = 1; for(int j = 1; j < i; ++ j){ C[i][j] = (C[i-1][j] + C[i-1][j-1]) % P; } } fac[0] = 1; for(int i = 1; i <= m; ++ i){ fac[i] = fac[i-1] * i % P; F[0] = 1; ll pw = 1; for(int k = 1; k <= n; ++ k){ pw = pw * i % P; F[k] = 0; for(int j = 1; j <= k; ++ j){ F[k] = (F[k] + C[k-1][j-1] * F[j-1] % P * F[k-j]) % P; } F[k] = F[k] * pw % P; } y[i] = F[n]; } p[0] = 1; for(int i = 1; i <= m; ++ i){ for(int j = 1; j <= i; ++ j){ q[j] = p[j-1]; p[j-1] = p[j-1] * (P - i) % P; } for(int j = 0; j <= i; ++ j){ p[j] = (p[j] + q[j]) % P; } } for(int i = 1; i <= m; ++ i){ ll val = y[i] * qp(fac[i-1], P-2) % P * qp(fac[m-i], P-2) % P; if((m - i) & 1){ val = P - val; } for(int j = 0; j <= m; ++ j){ q[j] = p[j]; } for(int j = m - 1; j >= 0; -- j){ ll r = q[j+1]; f[j] = (f[j] + r * val) % P; q[j] = (q[j] + r * i) % P; } } ll ans = 0; for(int i = lim; i < m; ++ i){ ans = (ans + f[i]) % P; } cout << ans << '\n'; }
15. AGC030C - Coloring Torus
这你都不会?
首先
对于
点击查看代码
//AT_agc030_c #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 510; int k, a[N][N]; void solve(){ // freopen(".out", "w", stdout); scanf("%d", &k); if(k <= 500){ printf("%d\n", k); for(int i = 1; i <= k; ++ i){ for(int j = 1; j <= k; ++ j){ printf("%d ", j); } puts(""); } } else { puts("500"); for(int i = 1; i <= 500; ++ i){ for(int j = 1, p = i; j <= 500; ++ j){ a[j][p] = i; if(i + 500 <= k && j % 2 == 0){ a[j][p] = i + 500; } ++ p; if(p > 500){ p -= 500; } } } for(int i = 1; i <= 500; ++ i){ for(int j = 1; j <= 500; ++ j){ printf("%d ", a[i][j]); } puts(""); } } }
16. NOI2020 - 超现实树
离正解就差一点/ll。
结论:若一棵树中存在一个节点的两个子结点均有子结点,则这棵树无效。
结论成立,因为这些数不会对任何其中一个儿子为空/其中一个儿子为叶子的树产生贡献。
有了这个结论然后使用四叉 trie 维护其中一个儿子为空/其中一个儿子为叶子共四种情况。然后从叶子往根 dp 一下即可。
点击查看代码
//P6776 #include <bits/stdc++.h> using namespace std; const int N = 2e6 + 10; int T, m, n; vector<pair<int, int> > g[N]; int ch[N][4], tot = 1, ok[N]; int sc(pair<int, int> x){ return (x.first ? 1 : 0) + (x.second ? 1 : 0); } void dfs(int i, int p, int q){ if(g[i][q].first && g[i][q].second){ if(!sc(g[i][g[i][q].first])){ if(!ch[p][2]) ch[p][2] = ++ tot; dfs(i, ch[p][2], g[i][q].second); } if(!sc(g[i][g[i][q].second])){ if(!ch[p][3]) ch[p][3] = ++ tot; dfs(i, ch[p][3], g[i][q].first); } } else if(g[i][q].first){ if(!ch[p][0]) ch[p][0] = ++ tot; dfs(i, ch[p][0], g[i][q].first); } else if(g[i][q].second){ if(!ch[p][1]) ch[p][1] = ++ tot; dfs(i, ch[p][1], g[i][q].second); } else { ok[p] = 1; } } int main(){ scanf("%d", &T); while(T--){ scanf("%d", &m); for(int i = 1; i <= m; ++ i){ scanf("%d", &n); g[i].resize(n + 1); for(int j = 1; j <= n; ++ j){ scanf("%d%d", &g[i][j].first, &g[i][j].second); } bool flg = 1; for(int j = 1; j <= n; ++ j){ if(sc(g[i][g[i][j].first]) && sc(g[i][g[i][j].second])){ flg = 0; break; } } if(flg){ dfs(i, 1, 1); } } for(int i = tot; i >= 1; -- i){ if(ok[ch[i][0]] && ok[ch[i][1]] && ok[ch[i][2]] && ok[ch[i][3]]){ ok[i] = 1; } } puts(ok[1] ? "Almost Complete" : "No"); for(int i = 0; i <= tot; ++ i){ ch[i][0] = ch[i][1] = ch[i][2] = ch[i][3] = ok[i] = 0; } tot = 1; } return 0; }
17. NOI2020 - 制作菜品
显然当
当
点击查看代码
//P6775 #include <bits/stdc++.h> using namespace std; const int N = 510; int T, n, m, k, ok[N]; pair<int, int> d[N], p[N]; bitset<N*N*20> b[N]; void solve(int cnt, int l, int r){ int tp = l; for(int i = 1; i <= cnt; ++ i){ sort(d + tp, d + r + 1); if(d[tp].first > k){ printf("%d %d", d[tp].second, k); d[tp].first -= k; } else if(d[tp].first == k){ printf("%d %d", d[tp].second, k); d[tp].first = 0; ++ tp; } else { printf("%d %d ", d[tp].second, d[tp].first); printf("%d %d", d[r].second, k - d[tp].first); d[r].first -= (k - d[tp].first); d[tp].first = 0; ++ tp; } puts(""); } } int main(){ scanf("%d", &T); while(T--){ scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= n; ++ i){ scanf("%d", &d[i].first); d[i].second = i; } if(m >= n - 1){ solve(m, 1, n); } else { int tmp = n * k + 3; b[0].reset(); b[0].set(tmp); for(int i = 1; i <= n; ++ i){ if(d[i].first >= k){ b[i] = b[i-1] | (b[i-1] << d[i].first - k); } else { b[i] = b[i-1] | (b[i-1] >> k - d[i].first); } } if(b[n].test(tmp - k)){ int flg = tmp - k; int l = 0, r = 0; for(int i = n; i >= 1; -- i){ if(b[i-1].test(flg)){ continue; } for(int j = 1; j <= n; ++ j){ if(!ok[i] && b[i-1].test(flg-d[i].first+k)){ ok[i] = 1; flg -= d[i].first-k; ++ r; } } } for(int i = 1; i <= n; ++ i){ if(ok[i] == 1){ p[++l] = d[i]; } else { p[++r] = d[i]; } ok[i] = 0; } memcpy(d, p, sizeof(p)); solve(l-1, 1, l); solve(r-l-1, l+1, r); } else { puts("-1"); } } } return 0; }
18. LuoguP2633 - Count on a tree
使用主席树维护每个节点到根的值域线段树,接着使用类似树上差分的方法查询即可。
点击查看代码
//P2633 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 1e5 + 10; int n, m, rg, a[N], b[N], anc[N][20], dep[N]; int t[N*20], ls[N*20], rs[N*20], rt[N], cnt = 1; vector<int> g[N]; int add(int p, int l, int r, int x){ int nw = ++ cnt; tie(t[nw], ls[nw], rs[nw]) = tie(t[p], ls[p], rs[p]); if(l == r){ ++ t[nw]; } else { int mid = l + r >> 1; if(x <= mid){ ls[nw] = add(ls[nw], l, mid, x); } else { rs[nw] = add(rs[nw], mid+1, r, x); } t[nw] = t[ls[nw]] + t[rs[nw]]; } return nw; } int qry(int p, int q, int x, int y, int l, int r, int k){ if(l == r){ return l; } else { int mid = l + r >> 1; int val = t[ls[p]] + t[ls[q]] - t[ls[x]] - t[ls[y]]; if(val >= k){ return qry(ls[p], ls[q], ls[x], ls[y], l, mid, k); } else { return qry(rs[p], rs[q], rs[x], rs[y], mid+1, r, k-val); } } } void dfs(int x, int fa){ anc[x][0] = fa; dep[x] = dep[fa] + 1; rt[x] = add(rt[fa], 1, rg, a[x]); for(int i = 1; i < 20; ++ i){ anc[x][i] = anc[anc[x][i-1]][i-1]; } for(int i : g[x]){ if(i != fa){ dfs(i, x); } } } int lca(int x, int y){ if(dep[x] < dep[y]){ swap(x, y); } for(int i = 19; i >= 0; -- i){ if(dep[anc[x][i]] >= dep[y]){ x = anc[x][i]; } } if(x == y){ return x; } for(int i = 19; i >= 0; -- i){ if(anc[x][i] != anc[y][i]){ x = anc[x][i]; y = anc[y][i]; } } return anc[x][0]; } void solve(){ scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); b[i] = a[i]; } sort(b + 1, b + n + 1); rg = unique(b + 1, b + n + 1) - b - 1; for(int i = 1; i <= n; ++ i){ a[i] = lower_bound(b + 1, b + rg + 1, a[i]) - b; } for(int i = 1; i < n; ++ i){ int x, y; scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } rt[0] = ++ cnt; dfs(1, 0); int ls = 0; while(m--){ int u, v, k; scanf("%d%d%d", &u, &v, &k); u ^= ls; int p = lca(u, v); ls = b[qry(rt[u], rt[v], rt[p], rt[anc[p][0]], 1, rg, k)]; printf("%d\n", ls); } }
19. 国家集训队 - middle
考虑二分答案。让序列中的
于是我们可以使用两棵主席树,分别维护当
查询时则对于二分到的每个
点击查看代码
//P2839 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 20010, M = 400; int lrt[N], rrt[N], t[N*M], tag[N*M], ls[N*M], rs[N*M], cnt; int n, a[N], q, la, pos[N]; pair<int, int> b[N]; int build(int l, int r, int op){ int p = ++ cnt; if(l == r){ t[p] = op ? l : n - l + 1; } else { int mid = l + r >> 1; ls[p] = build(l, mid, op); rs[p] = build(mid+1, r, op); t[p] = max(t[ls[p]], t[rs[p]]); } return p; } int nnd(int p){ ++ cnt; tie(t[cnt], tag[cnt], ls[cnt], rs[cnt]) = tie(t[p], tag[p], ls[p], rs[p]); return cnt; } void psd(int p){ if(!tag[p]){ return; } ls[p] = nnd(ls[p]); rs[p] = nnd(rs[p]); tag[ls[p]] += tag[p]; tag[rs[p]] += tag[p]; t[ls[p]] += tag[p]; t[rs[p]] += tag[p]; tag[p] = 0; } int add(int p, int l, int r, int ql, int qr){ p = nnd(p); if(ql <= l && r <= qr){ tag[p] -= 2; t[p] -= 2; } else { int mid = l + r >> 1; psd(p); if(ql <= mid){ ls[p] = add(ls[p], l, mid, ql, qr); } if(mid < qr){ rs[p] = add(rs[p], mid+1, r, ql, qr); } t[p] = max(t[ls[p]], t[rs[p]]); } return p; } int ask(int p, int l, int r, int ql, int qr){ if(ql <= l && r <= qr){ return t[p]; } else { int mid = l + r >> 1; psd(p); int ans = -2e9; if(ql <= mid){ ans = max(ans, ask(ls[p], l, mid, ql, qr)); } if(mid < qr){ ans = max(ans, ask(rs[p], mid+1, r, ql, qr)); } return ans; } } bool chk(int a, int b, int c, int d, int k){ -- k; int val = 0; val += ask(lrt[k], 1, n, c, d) - ask(lrt[k], 1, n, b, b); val += ask(rrt[k], 1, n, a, b) - ask(rrt[k], 1, n, b+1, b+1); return val >= 0; } void solve(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); b[i] = make_pair(a[i], i); } sort(b + 1, b + n + 1); for(int i = 1; i <= n; ++ i){ a[i] = lower_bound(b + 1, b + n + 1, make_pair(a[i], i)) - b; pos[a[i]] = i; } lrt[0] = build(1, n, 1); rrt[0] = build(1, n, 0); for(int i = 1; i <= n; ++ i){ lrt[i] = add(lrt[i-1], 1, n, pos[i], n); rrt[i] = add(rrt[i-1], 1, n, 1, pos[i]); } scanf("%d", &q); while(q--){ int p[4]; scanf("%d%d%d%d", &p[0], &p[1], &p[2], &p[3]); p[0] = (p[0] + la) % n; p[1] = (p[1] + la) % n; p[2] = (p[2] + la) % n; p[3] = (p[3] + la) % n; sort(p, p + 4); int l = 1, r = n; while(l < r){ int mid = l + r + 1 >> 1; if(chk(p[0] + 1, p[1] + 1, p[2] + 1, p[3] + 1, mid)){ l = mid; } else { r = mid - 1; } } la = b[l].first; printf("%d\n", la); } }
20. HEOI/TJOI2016 - 字符串
我都会,这不降紫?
问题相当于求最长的
考虑二分答案。设目前二分到
问题转化为
点击查看代码
//P4094 #include <bits/stdc++.h> using namespace std; typedef long long ll; void solve();int main(){ solve(); return 0; } const int N = 1e5 + 10, M = 30; int n, q; int sa[N], rk[N], cnt[N], pre[N], h[N]; char s[N]; void suf(){ int m = 26; for(int i = 1; i <= n; ++ i){ rk[i] = s[i] - 'a' + 1; ++ cnt[rk[i]]; } for(int i = 2; i <= m; ++ i){ cnt[i] += cnt[i-1]; } for(int i = n; i >= 1; -- i){ sa[cnt[rk[i]]--] = i; } for(int k = 1; k <= n; k <<= 1){ int tot = 0; for(int i = n - k + 1; i <= n; ++ i){ pre[++tot] = i; } for(int i = 1; i <= n; ++ i){ if(sa[i] > k){ pre[++tot] = sa[i] - k; } } for(int i = 1; i <= m; ++ i){ cnt[i] = 0; } for(int i = 1; i <= n; ++ i){ ++ cnt[rk[i]]; } for(int i = 2; i <= m; ++ i){ cnt[i] += cnt[i-1]; } for(int i = n; i >= 1; -- i){ sa[cnt[rk[pre[i]]]--] = pre[i]; pre[i] = 0; } swap(rk, pre); tot = 1, rk[sa[1]] = 1; for(int i = 2; i <= n; ++ i){ if(pre[sa[i]] == pre[sa[i-1]] && pre[sa[i]+k] == pre[sa[i-1]+k]){ rk[sa[i]] = tot; } else { rk[sa[i]] = ++ tot; } } if(tot == n){ break; } m = tot; } int k = 0; for(int i = 1; i <= n; ++ i){ if(rk[i] == 1){ h[1] = 0; } if(k){ -- k; } int j = sa[rk[i]-1]; while(j+k <= n && i+k <= n && s[i+k] == s[j+k]){ ++ k; } h[rk[i]] = k; } } int st[N][20]; void table(){ for(int i = 1; i <= n; ++ i){ st[i][0] = h[i]; } for(int i = 1; i < 20; ++ i){ for(int j = 1; j + (1 << i) - 1 <= n; ++ j){ st[j][i] = min(st[j][i-1], st[j+(1<<i-1)][i-1]); } } } int qry(int l, int r){ if(l == r){ return 1e9; } if(l > r){ swap(l, r); } ++ l; int k = 31 ^ __builtin_clz(r - l + 1); return min(st[l][k], st[r-(1<<k)+1][k]); } int t[N*M], ls[N*M], rs[N*M], rt[N], tot; int add(int p, int l, int r, int x){ ++ tot; tie(t[tot], ls[tot], rs[tot]) = tie(t[p], ls[p], rs[p]); p = tot; if(l == r){ ++ t[p]; } else { int mid = l + r >> 1; if(x <= mid){ ls[p] = add(ls[p], l, mid, x); } else { rs[p] = add(rs[p], mid+1, r, x); } t[p] = t[ls[p]] + t[rs[p]]; } return p; } int sum(int p, int q, int l, int r, int ql, int qr){ if(qr < l || r < ql){ return 0; } else if(ql <= l && r <= qr){ return t[p] - t[q]; } else { int mid = l + r >> 1; return sum(ls[p], ls[q], l, mid, ql, qr) + sum(rs[p], rs[q], mid+1, r, ql, qr); } } void seg(){ for(int i = 1; i <= n; ++ i){ rt[i] = add(rt[i-1], 1, n, rk[i]); } } bool chk(int a, int b, int c, int d, int k){ int l = a, r = b - k + 1; int x, y; int L = 1, R = rk[c]; while(L < R){ int mid = L + R >> 1; if(qry(mid, rk[c]) >= k){ R = mid; } else { L = mid + 1; } } x = L; L = rk[c], R = n; while(L < R){ int mid = L + R + 1 >> 1; if(qry(rk[c], mid) >= k){ L = mid; } else { R = mid - 1; } } y = L; return sum(rt[r], rt[l-1], 1, n, x, y); } void solve(){ scanf("%d%d", &n, &q); scanf("%s", s + 1); suf(); seg(); table(); while(q--){ int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d); int l = 0, r = min(d - c + 1, b - a + 1); while(l < r){ int mid = l + r + 1 >> 1; if(chk(a, b, c, d, mid)){ l = mid; } else { r = mid - 1; } } printf("%d\n", l); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步