2024.07 别急记录
1. CEOI2023 - Balance
考虑
对于
点击查看代码
// Problem: P9731 [CEOI2023] Balance // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P9731 // Memory Limit: 1 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; const int N = 6e5 + 10, M = 2e6 + 10; int n, m, t; vector<int> a[N]; int ind[N], cnt, id[N], cb[N]; int hd[N], fr[M], to[M], nx[M], tot = 1; int vis[N], ban[M]; int cl[N], cr[N]; vector<int> st; void add(int x, int y){ fr[++tot] = x; to[tot] = y; nx[tot] = hd[x]; hd[x] = tot; } void dfs(int x){ vis[x] = 1; for(int &i = hd[x]; i; i = nx[i]){ int y = to[i]; if(ban[i]){ continue; } int eg = i; ban[i] = ban[i^1] = 1; dfs(y); st.push_back(eg); } } void solve(int l, int r){ if(l == r){ return; } int mid = l + r >> 1; for(int i = 1; i <= n; ++ i){ for(int j = l; j <= r; ++ j){ if(!ind[a[i][j]]){ id[a[i][j]] = ++ cnt; cb[cnt] = a[i][j]; } ++ ind[a[i][j]]; } } for(int i = 1; i <= n; ++ i){ for(int j = l; j <= r; ++ j){ add(id[a[i][j]], i+cnt); add(i+cnt, id[a[i][j]]); } } for(int i = 1; i <= cnt; ++ i){ if(ind[cb[i]] & 1){ add(i, n+cnt+1); add(n+cnt+1, i); } } for(int i = 1; i <= cnt + n + 1; ++ i){ if(!vis[i]){ dfs(i); } } reverse(st.begin(), st.end()); for(int i = 1; i <= n; ++ i){ cl[i] = l, cr[i] = r; } for(auto i : st){ int x = fr[i], y = to[i]; if(x <= cnt && y <= n + cnt){ a[y-cnt][cl[y-cnt]++] = cb[x]; } if(x <= n + cnt && y <= cnt){ a[x-cnt][cr[x-cnt]--] = cb[y]; } } vector<int> ().swap(st); for(int i = 1; i <= cnt; ++ i){ ind[cb[i]] = id[cb[i]] = 0; cb[i] = 0; } for(int i = 1; i <= cnt + n + 1; ++ i){ vis[i] = hd[i] = 0; } for(int i = 1; i <= tot; ++ i){ ban[i] = 0; } cnt = 0; tot = 1; solve(l, mid); solve(mid+1, r); } int main(){ int T = 1; while(T--){ scanf("%d%d%d", &n, &m, &t); for(int i = 1; i <= n; ++ i){ a[i].resize(m+5); for(int j = 1; j <= m; ++ j){ scanf("%d", &a[i][j]); } } solve(1, m); for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ printf("%d ", a[i][j]); } puts(""); } } return 0; }
2. CF1693C - Keshi in Search of AmShZ
题目转化为:给定一张有向图,求删边数+删边后最长路最小值。
考虑设
即删去所有劣于
其实可以直接按照 dij 的方式进行转移,
点击查看代码
//CF1693C #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int n, m; vector<int> g[N]; int vis[N]; int dis[N], ind[N]; int main(){ scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++ i){ int u, v; scanf("%d%d", &u, &v); g[v].push_back(u); ++ ind[u]; } memset(dis, 0x3f, sizeof(dis)); priority_queue<pair<int, int> > q; q.push(make_pair(0, n)); dis[n] = 0; while(!q.empty()){ int x = q.top().second; q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i : g[x]){ -- ind[i]; int val = dis[x] + ind[i] + 1; if(dis[i] > val){ dis[i] = val; q.push(make_pair(-dis[i], i)); } } } printf("%d\n", dis[1]); return 0; }
3. CF1693D - Decinc Dividing
设
则有四种转移:
- 当
时 ; - 当
时 ; - 当
时 ; - 当
时 。
那么初始化
一个常见的思路是使用数据结构维护,同时进行多个左端点的转移;或者使用双指针确定区间范围。这里发现两种都不太好做,有一种 trick:令左端点从右往左,若一次递推
我们考虑有解的
当然这题也有数据结构维护的做法。
点击查看代码
//CF1693D #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 10; int n, p[N], f[N][2]; long long ans = 0; int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &p[i]); } memset(f, -1, sizeof(f)); int la = n; for(int i = n; i >= 1; -- i){ f[i][0] = n+1; f[i][1] = 0; for(int j = i; j < n; ++ j){ int f0 = 0, f1 = n+1; if(p[j] < p[j+1]){ f0 = max(f0, f[j][0]); } if(f[j][0] > p[j+1]){ f1 = min(f1, p[j]); } if(p[j] > p[j+1]){ f1 = min(f1, f[j][1]); } if(f[j][1] < p[j+1]){ f0 = max(f0, p[j]); } if(f0 == f[j+1][0] && f1 == f[j+1][1]){ break; } f[j+1][0] = f0; f[j+1][1] = f1; if(f0 == 0 && f1 == n+1){ la = j; break; } } ans += la - i + 1; } printf("%lld\n", ans); return 0; }
4. AHOI2024 初中组 - 计数 / PA2021 - Od deski do deski
刚开始想
考虑对于一个序列如何判断可行:
于是设
,表示添加一个数使不可行变为可行。 ,表示添加一个数仍旧不可行。因为 不可行所以 ,第二维的值不会改变(这也就是为什么要分 )。 ,表示添加一个数仍旧可行。 ,表示添加了一个另外的数使得可行变成不可行。因为 可行所以 。
点击查看代码
// Problem: P10375 [AHOI2024 初中组] 计数 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P10375 // Memory Limit: 512 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; const int N = 3010; typedef long long ll; const ll P = 1e9 + 7; ll f[N][N], g[N][N]; int n, m; int main(){ int T = 1; while(T--){ scanf("%d%d", &n, &m); f[1][1] = m; for(int i = 2; i <= n; ++ i){ for(int j = 0; j <= i; ++ j){ g[i][j] = (g[i][j] + f[i-1][j] * j) % P; f[i][j] = (f[i][j] + f[i-1][j] * (m-j)) % P; g[i][j] = (g[i][j] + g[i-1][j] * j) % P; f[i][j+1] = (f[i][j+1] + g[i-1][j] * (m-j)) % P; } } ll ans = 0; for(int j = 1; j <= n; ++ j){ ans = (ans + g[n][j]) % P; } printf("%lld\n", ans); } return 0; }
5. JOISC2014 - 歴史の研究
回滚莫队。之前不会现在想了想发现挺简单。
一样分块,两端在同一块内的暴力做。假设目前右端点
点击查看代码
// Problem: 歴史の研究 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/AT_joisc2014_c // Memory Limit: 512 MB // Time Limit: 4000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; int n, m, q, a[N], b[N], bl; int cnt[N]; ll ans[N]; struct qry{ int l, r, id; } qr[N]; vector<qry> v[N]; bool cmp(qry x, qry y){ return x.r < y.r; } int main(){ int T = 1; while(T--){ scanf("%d%d", &n, &q); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); b[i] = a[i]; } sort(b + 1, b + n + 1); m = unique(b + 1, b + n + 1) - b - 1; for(int i = 1; i <= n; ++ i){ a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b; } bl = n / sqrt(q); for(int i = 1; i <= q; ++ i){ scanf("%d%d", &qr[i].l, &qr[i].r); qr[i].id = i; v[(qr[i].l-1)/bl].push_back(qr[i]); } int mx = (n-1) / bl; for(int i = 0; i <= mx; ++ i){ sort(v[i].begin(), v[i].end(), cmp); int br = min(n, (i+1) * bl); int r = br; ll now = 0; for(auto nw : v[i]){ if(nw.r <= br){ for(int j = nw.l; j <= nw.r; ++ j){ ++ cnt[a[j]]; ans[nw.id] = max(ans[nw.id], (ll)b[a[j]] * cnt[a[j]]); } for(int j = nw.l; j <= nw.r; ++ j){ -- cnt[a[j]]; } } else { while(r < nw.r){ ++ r; ++ cnt[a[r]]; now = max(now, (ll)b[a[r]] * cnt[a[r]]); } ans[nw.id] = now; for(int j = br; j >= nw.l; -- j){ ++ cnt[a[j]]; ans[nw.id] = max(ans[nw.id], (ll)b[a[j]] * cnt[a[j]]); } for(int j = br; j >= nw.l; -- j){ -- cnt[a[j]]; } } } memset(cnt, 0, sizeof(cnt)); } for(int i = 1; i <= q; ++ i){ printf("%lld\n", ans[i]); } } return 0; }
6. UNR #6 - 稳健型选手
100 分懒得写,来点 70。
考虑一个方案合法当且仅当区间内任意前缀先手取的至多比后手取的多
考虑反悔贪心:先手取右侧接下来的两个,然后丢掉一个取过的最小值;同样也可以先手不取左侧接下来的两个,在取一个没取过的最大值。于是可以进行回滚莫队
点击查看代码
// Problem: C. 稳健型选手 // Contest: UOJ - UOJ NOI Round #6 Day1 // URL: https://uoj.ac/contest/77/problem/749 // Memory Limit: 512 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 10; int n, q, Q, a[N], L[N], R[N]; ll ans[N]; struct qry{ int l, r, id; } qr[N]; vector<int> v[N]; bool cmp(int x, int y){ return qr[x].r < qr[y].r; } pair<int, int> sta[N*4]; int tp; priority_queue<int> ou, od; priority_queue<int, vector<int>, greater<int> > in; void solve(){ int bl = 510; int mx = (n-1) / bl; for(int i = 1; i <= q; ++ i){ v[(qr[i].l-1)/bl].push_back(i); } for(int p = 0; p <= mx; ++ p){ sort(v[p].begin(), v[p].end(), cmp); int br = (p+1) * bl; int r = br; ll now = 0; for(int pp : v[p]){ auto &x = qr[pp]; if(x.r <= br){ ll tmp = 0; for(int i = x.r-1; i >= x.l; i -= 2){ ou.push(a[i]); ou.push(a[i+1]); tmp += ou.top(); ou.pop(); } ou = {}; ans[x.id] += tmp; } else { while(r < x.r){ r += 2; in.push(a[r-1]); in.push(a[r]); int x = in.top(); now += a[r-1] + a[r] - x; ou.push(x); in.pop(); } od = ou; ll tmp = now; for(int i = br - 1; i >= x.l; i -= 2){ od.push(a[i]); od.push(a[i+1]); tmp += od.top(); od.pop(); } ans[x.id] += tmp; } } ou = {}; od = {}; in = {}; vector<int> ().swap(v[p]); } } int main(){ scanf("%d%d", &n, &Q); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); } for(int i = 1; i <= Q; ++ i){ scanf("%d%d", &L[i], &R[i]); if((R[i] - L[i] + 1) & 1){ ans[i] += a[R[i]]; -- R[i]; } } q = 0; for(int i = 1; i <= Q; ++ i){ if((L[i] & 1) && (L[i] <= R[i])){ qr[++q] = { L[i], R[i], i }; } } solve(); q = 0; for(int i = 1; i <= Q; ++ i){ if((!(L[i] & 1)) && (L[i] <= R[i])){ qr[++q] = { L[i]-1, R[i]-1, i }; } } for(int i = 1; i < n; ++ i){ a[i] = a[i+1]; } -- n; solve(); for(int i = 1; i <= Q; ++ i){ printf("%lld\n", ans[i]); } return 0; }
7. LG5591 - 小猪佩奇学数学
唉,单位根反演。
证明:
- 当
时 ,显然成立; - 当
时 右边构成 个 的环,显然和为 ,成立。
所以有:
由于
点击查看代码
// Problem: P5591 小猪佩奇学数学 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P5591 // Memory Limit: 500 MB // Time Limit: 3000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 998244353, iG = 3; const int N = (1<<20) + 10; ll n, p, k, w[N]; ll qp(ll x, ll y){ x %= P; ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } int main(){ scanf("%lld%lld%lld", &n, &p, &k); w[0] = 1; w[1] = qp(iG, (P-1) / k); for(int i = 2; i < k; ++ i){ w[i] = w[i-1] * w[1] % P; } ll ans = 0; for(int t = 1; t < k; ++ t){ ll tmp = (qp(p+1, n) + P - qp(p*w[t]+1, n) * w[t] % P) % P; ans = (ans + tmp * qp(1+P-w[t], P-2)) % P; } ans = (ans + n * p % P * qp(p+1, n-1) + qp(p+1, n)) % P; ans = ans * qp(k, P-2) % P; ans = (ans + P - qp(p+1, n)) % P; printf("%lld\n", ans); return 0; }
8. loj6485 - LJJ 学二项式定理
同上一题使用单位根反演得答案为:
点击查看代码
// Problem: #6485. LJJ 学二项式定理 // Contest: LibreOJ // URL: https://loj.ac/p/6485 // Memory Limit: 256 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 998244353, iG = 3; ll n, s, a[4], w[4]; ll qp(ll x, ll y){ x %= P; ll ans = 1; while(y){ if(y & 1){ ans = ans * x % P; } x = x * x % P; y >>= 1; } return ans; } int main(){ w[0] = 1; w[1] = qp(iG, (P-1) / 4); w[2] = w[1] * w[1] % P; w[3] = w[2] * w[1] % P; int T = 1; scanf("%d", &T); while(T--){ scanf("%lld%lld%lld%lld%lld%lld", &n, &s, &a[0], &a[1], &a[2], &a[3]); ll ans = 0; for(int i = 0; i <= 3; ++ i){ for(int j = 0; j <= 3; ++ j){ ans = (ans + a[i] * w[(4-i*j%4)%4] % P * qp(s*w[j]+1, n)) % P; } } printf("%lld\n", ans * qp(4, P-2) % P); } return 0; }
9. UNR #1 - Jakarta Skyscrapers
首先可以实现一个操作:给定一个
,递归下去构造出所有需要的 。- 把
二进制拆分,构造 。
然后就可以先用辗转相除构造出
无解情况:
点击查看代码
// Problem: A. Jakarta Skyscrapers // Contest: UOJ - UOJ NOI Round #1 Day2 // URL: https://uoj.ac/contest/32/problem/216 // Memory Limit: 256 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; ll a, b, c; map<ll, int> mp; pair<ll, ll> ans[500]; int cnt = 0; void add(ll x, ll y){ if(!mp[x] || !mp[y]){ printf("waa %d %d %d %d\n", x, mp[x], y, mp[y]); } if(!mp[x-y]){ mp[x-y] = 1; ans[++cnt] = make_pair(x, y); } } void bld(ll x, ll y, ll k){ add(x, y); if(k == 1) return; for(ll i = 1; i*2 <= k; i *= 2){ add(x-y*i, y*i); add(x, x-y*i*2); } ll nw = x; for(int i = 62; i >= 0; -- i){ if(k & (1ll << i)){ add(nw, y*(1ll<<i)); nw -= y*(1ll << i); } } add(x, nw); } void zz(ll x, ll y){ // printf("%d %d %d\n", x, y, x/y); if(y == 1 || x == 1){ return; } bld(x, y, x/y); zz(y, x%y); } int main(){ cin >> a >> b >> c; if(a < b){ swap(a, b); } if(a == c || b == c){ puts("0"); return 0; } ll g = __gcd(a, b); if(c % g || c > a){ puts("-1"); return 0; } a /= g; b /= g; c /= g; mp[a] = mp[b] = 1; zz(a, b); bld(a, 1, a-c); printf("%d\n", cnt); for(int i = 1; i <= cnt; ++ i){ printf("%lld %lld\n", ans[i].first * g, ans[i].second * g); } return 0; }
10. UNR #8 - 兵棋
不妨串的第一位为
点击查看代码
// Problem: A. 兵棋 // Contest: UOJ - UOJ NOI Round #8 Day2 // URL: https://uoj.ac/contest/91/problem/890 // Memory Limit: 1024 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 6, K = 204; const int P = 998244353; typedef long long ll; int n, k; char s[N]; int f[N][K], g[N][K]; void upd(int &x, int y){ x += y; if(x >= P){ x -= P; } } int calc(){ memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g)); f[1][0] = g[1][0] = 1; for(int i = 2; i <= n; ++ i){ for(int j = 0; j <= k; ++ j){ if(s[i] == '?' || s[i] == '0'){ if(j){ upd(g[i][j-1], g[i-1][j]); upd(f[i][j-1], f[i-1][j]); } else { upd(g[i][0], g[i-1][0]); upd(f[i][0], f[i-1][0]); upd(f[i][0], g[i-1][0]); } } if(s[i] == '?' || s[i] == '1'){ if(j < k){ upd(g[i][j+1], g[i-1][j]); upd(f[i][j+1], f[i-1][j]); } else { upd(g[i][k], g[i-1][k]); upd(f[i][k], f[i-1][k]); upd(f[i][k], g[i-1][k]); } } } } int ans = 0; for(int i = 0; i <= k; ++ i){ upd(ans, f[n][i]); } return ans; } int main(){ scanf("%d%d", &n, &k); scanf("%s", s+1); int ans = 0; if(s[1] != '1'){ upd(ans, calc()); } if(s[1] != '0'){ for(int i = 1; i <= n; ++ i){ if(s[i] == '0'){ s[i] = '1'; } else if(s[i] == '1'){ s[i] = '0'; } } upd(ans, calc()); } printf("%d\n", ans); return 0; }
11. BZOJ3028/LGP10780 - 食物
考虑生成函数,
化简得
即答案为
点击查看代码
// Problem: P10780 BZOJ3028 食物 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P10780 // Memory Limit: 512 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 510 * 4; const ll P = 10007; int n; char t[N]; int s[3][N]; void dv(int x, int v){ for(int i = n-1; i >= 0; -- i){ s[x][i-1] += s[x][i] % v * 10; s[x][i] /= v; } } ll clc(int x){ ll ans = 0; for(int i = n-1; i >= 0; -- i){ ans = (ans * 10 + s[x][i]) % P; } return ans; } int main(){ scanf("%s", t); n = strlen(t); for(int i = 0; i < n; ++ i){ s[0][i] = s[1][i] = s[2][i] = t[n-i-1] - '0'; } s[1][0] += 2; s[2][0] += 1; int sm = 0; for(int i = 0; i < n; ++ i){ sm += s[0][i]; } dv((s[0][0]&1)*2, 2); dv(sm%3, 3); printf("%lld\n", clc(0) * clc(1) * clc(2) % P); return 0; }
12. LGP2012 - 拯救世界2
由于这题序列有序,所以不能使用 OGF,应使用 EGF。
EGF 定义:
为什么这样定义?考虑
那么原题的 EGF 分别为:
- ACGT:
; - 阳:
; - 阴:
;
答案为
点击查看代码
// Problem: P2012 拯救世界2 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P2012 // Memory Limit: 125 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 1e9; ll 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; } int main(){ while(scanf("%lld", &n) && n){ if(n <= 6){ ll ans = qp(12, n) - 4 * qp(8, n) + 6 * qp(4, n) + qp(-4, n); ans /= 256; printf("%lld\n", ans % P); } else { n -= 4; ll ans = 81 * qp(12, n) - 64 * qp(8, n) + 6 * qp(4, n) + qp(P-4, n); printf("%lld\n", (ans % P + P) % P); } } return 0; }
13. LG4844 - LJJ爱数数
由题意得
两边同时加
考虑莫反,很难不求出答案为
很难不发现后面式子中含有
点击查看代码
// Problem: P4844 LJJ爱数数 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P4844 // Memory Limit: 250 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 10; ll n; int mu[N], p[N], c, v[N]; int main(){ n = 1e6; mu[1] = 1; for(int i = 2; i <= n; ++ i){ if(!v[i]){ p[++c] = i; mu[i] = -1; } for(int j = 1; j <= c && i * p[j] <= n; ++ j){ v[i*p[j]] = 1; if(i % p[j]){ mu[i*p[j]] = -mu[i]; } else { mu[i*p[j]] = 0; break; } } } cin >> n; ll m = sqrt(n); ll ans = 0; for(ll d = 1; d <= m; ++ d){ ll tmp = 0; for(ll i = 1; i <= m/d; ++ i){ tmp += min(i-1, n/i/d/d-i); } ans += tmp * mu[d]; } cout << ans * 2 + 1; return 0; }
14. HNOI2014 - 江南乐
考虑
考虑数论分块,对于
点击查看代码
// Problem: P3235 [HNOI2014] 江南乐 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P3235 // Memory Limit: 125 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100010; int n, F, f[N]; int calc(int i, int m){ int tmp = 0; if((m - i % m) & 1){ tmp ^= f[i/m]; } if((i % m) & 1){ tmp ^= f[i/m+1]; } return tmp; } int main(){ int T = 1; scanf("%d%d", &T, &F); for(int i = 0; i < F; ++ i){ f[i] = 0; } n = 100000; for(int i = F; i <= n; ++ i){ int v = 0; for(int l = 2, r; l <= i; l = r + 1){ r = i / (i / l); v |= (1 << calc(i, l)); if(l != r){ v |= (1 << calc(i, l+1)); } } for(int j = 0; j < 20; ++ j){ if(!(v & (1 << j))){ f[i] = j; break; } } } while(T--){ scanf("%d", &n); int ans = 0, a = 0; while(n--){ scanf("%d", &a); ans ^= f[a]; } printf("%d ", ans ? 1 : 0); } puts(""); return 0; }
15. CF1443E - Long Permutation
发现
点击查看代码
// Problem: Long Permutation // Contest: Luogu // URL: https://www.luogu.com.cn/problem/CF1443E // Memory Limit: 250 MB // Time Limit: 4000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 10; int n, q, la[25]; ll sum = 1, fac[25]; void upd(int len, ll val){ static int k[25]; memset(k, 0, sizeof(k)); ll nw = 1; for(int i = 1; i <= len; ++ i){ int p = 1; while(nw + fac[len-i] <= val){ nw += fac[len-i]; ++ p; } for(int j = 1, c = 0; j <= len; ++ j){ if(!k[j]){ ++ c; if(c == p){ la[i] = j; k[j] = 1; } } } } } ll calc(int x){ if(x < n - 15){ return (ll)x * (x+1) / 2; } else { ll ans = (ll)(n-16) * (n-15) / 2; for(int i = n-15; i <= x; ++ i){ ans += la[i-n+16] + (n-16); } return ans; } } int main(){ fac[0] = 1; for(int i = 1; i < 20; ++ i){ fac[i] = fac[i-1] * i; } scanf("%d%d", &n, &q); if(n <= 16){ upd(n, sum); } else { upd(16, sum); } while(q--){ int op, x, l, r; scanf("%d", &op); if(op == 1){ scanf("%d%d", &l, &r); ll ans = 0; if(n <= 16){ for(int i = l; i <= r; ++ i){ ans += la[i]; } } else { ans = calc(r) - calc(l-1); } printf("%lld\n", ans); } else { scanf("%d", &x); sum += x; if(n <= 16){ upd(n, sum); } else { upd(16, sum); } } } return 0; }
16. CF1700E - Serega the Pirate
发现一个合法的矩阵等价于对于一个
观察到对于一个不合法的格子,它或它的四周一定有个格子要被换掉,所以可以枚举这五个格子以及另一个被换掉的格子,然后计算这两个格子交换后是否合法。具体可以记录每个格子是否合法以及总合法格子数,每次交换后暴力询问这两个格子周围格子状态是否改变。
点击查看代码
<details> <summary>点击查看代码</summary> ```cpp // Problem: Serega the Pirate // Contest: Luogu // URL: https://www.luogu.com.cn/problem/CF1700E // Memory Limit: 250 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 4e5 + 10; const int dx[5] = {-1, 0, 0, 1, 0}, dy[5] = {0, -1, 1, 0, 0}; vector<int> a[N], ok[N], vs[N]; int n, m, cnt; bool chk(int x, int y){ if(x <= 0 || y <= 0 || x > n || y > m){ return 0; } for(int k = 0; k < 4; ++ k){ if(a[x+dx[k]][y+dy[k]] < a[x][y]){ return 1; } } if(a[x][y] == 1){ return 1; } return 0; } bool chg(int px, int py, int x, int y){ swap(a[px][py], a[x][y]); int tp = 0, tmp = cnt; for(int i = 0; i <= 4; ++ i){ int p = px + dx[i], q = py + dy[i]; if(!vs[p][q]){ vs[p][q] = 1; if(chk(p, q) && !ok[p][q]){ ++ tmp; } else if(!chk(p, q) && ok[p][q]){ -- tmp; } } p = x + dx[i], q = y + dy[i]; if(!vs[p][q]){ vs[p][q] = 1; if(chk(p, q) && !ok[p][q]){ ++ tmp; } else if(!chk(p, q) && ok[p][q]){ -- tmp; } } } for(int i = 0; i <= 4; ++ i){ vs[px+dx[i]][py+dy[i]] = 0; vs[x+dx[i]][y+dy[i]] = 0; } swap(a[px][py], a[x][y]); return tmp == n*m; } int main(){ scanf("%d%d", &n, &m); for(int i = 0; i <= n + 1; ++ i){ a[i].resize(m+2); ok[i].resize(m+2); vs[i].resize(m+2); for(int j = 0; j <= m + 1; ++ j){ a[i][j] = 1e9; ok[i][j] = 0; } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ scanf("%d", &a[i][j]); } } int px, py; for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ ok[i][j] = chk(i, j); if(a[i][j] == 1){ ok[i][j] = 1; } if(ok[i][j]){ ++ cnt; } else { px = i; py = j; } } } if(cnt == n * m){ puts("0"); } else { int ans = 0; for(int k = 0; k <= 4; ++ k){ int ppx = px + dx[k], ppy = py + dy[k]; if(ppx <= 0 || ppy <= 0 || ppx > n || ppy > m){ continue; } for(int i = 1; i <= n; ++ i){ for(int j = 1; j <= m; ++ j){ bool flg = 1; for(int l = 0; l <= k; ++ l){ if(i == px + dx[l] && j == py + dy[l]){ flg = 0; break; } } if(flg){ ans += chg(ppx, ppy, i, j); } } } } if(ans){ printf("1 %d\n", ans); } else { puts("2"); } } return 0; }
17. CF1849F - XOR Partition
考虑
点击查看代码
// Problem: XOR Partition // Contest: Luogu // URL: https://www.luogu.com.cn/problem/CF1849F // Memory Limit: 500 MB // Time Limit: 7000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 200010, M = 31; int n, a[N]; int ch[N*M][2], siz[N*M], tot, id[N*M]; int fa[N]; vector<int> son[N], g[N]; int cl[N]; void ins(int x, int v){ int p = 0; for(int i = 29; i >= 0; -- i){ if(!ch[p][(a[x]>>i)&1]){ ch[p][(a[x]>>i)&1] = ++ tot; } p = ch[p][(a[x]>>i)&1]; siz[p] += v; } id[p] = x; } int qry(int x){ int p = 0; for(int i = 29; i >= 0; -- i){ if(siz[ch[p][(x>>i)&1]]){ p = ch[p][(x>>i)&1]; } else { p = ch[p][1-((x>>i)&1)]; } } return id[p]; } int gf(int x){ return x == fa[x] ? x : gf(fa[x]); } void mg(int x, int y){ x = gf(x); y = gf(y); if(son[x].size() < son[y].size()){ swap(x, y); } fa[y] = x; for(int j : son[y]){ son[x].push_back(j); } } void dfs(int x, int fa, int c){ cl[x] = c; for(int i : g[x]){ if(i != fa){ dfs(i, x, 1-c); } } } int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); ins(i, 1); fa[i] = i; son[i].push_back(i); } int cnt = 0; while(cnt < n - 1){ static int gx[N], gy[N]; for(int i = 1; i <= n; ++ i){ if(i == gf(i)){ int vl = 2147483647; for(int j : son[i]){ ins(j, -1); } for(int j : son[i]){ int k = qry(a[j]); // printf("%d %d\n", j, k); if(vl > (a[j] ^ a[k])){ vl = a[j] ^ a[k]; gx[i] = j; gy[i] = k; } } for(int j : son[i]){ ins(j, 1); } } } for(int i = 1; i <= n; ++ i){ if(i == gf(i) && gf(gx[i]) != gf(gy[i])){ mg(gx[i], gy[i]); g[gx[i]].push_back(gy[i]); g[gy[i]].push_back(gx[i]); ++ cnt; } } } dfs(1, 0, 1); for(int i = 1; i <= n; ++ i){ printf("%d", cl[i]); } puts(""); return 0; }
18. LGP7173 - 【模板】有负圈的费用流
先流满负权边,建流量相同费用相反的反边即可。
步骤:
- 使用上下界网络流的 trick 流满负权边,建流量相同费用相反的反边;
- 建超级源、汇点
相关的边,建 。 - 跑
的 mcmf,记录此时的最大流为 边的流量; - 删掉图中的所有附加边;
- 跑
的 mcmf,最大流为此时的流量加上第一次 mcmf 时记录的流量; - 最小费用为所有 mcmf 时刻的费用和。
点击查看代码
// Problem: P7173 【模板】有负圈的费用流 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P7173 // Memory Limit: 128 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 210, M = 1e5, inf = 1e9; int n, m, s, t, nd[N], mf, mc; int hd[N], eg[M], ln[M], cs[M], nx[M], tot = 1; int now[N], dis[N], vis[N]; void add(int u, int v, int w, int c){ eg[++tot] = v; ln[tot] = w; cs[tot] = c; nx[tot] = hd[u]; hd[u] = tot; } bool spfa(int s, int t){ queue<int> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); memcpy(now, hd, sizeof(hd)); dis[s] = 0; vis[s] = 1; q.push(s); while(!q.empty()){ int x = q.front(); q.pop(); vis[x] = 0; for(int i = hd[x]; i; i = nx[i]){ int y = eg[i], z = ln[i], c = cs[i]; if(z && dis[y] > dis[x] + c){ dis[y] = dis[x] + c; if(!vis[y]){ vis[y] = 1; q.push(y); } } } } return dis[t] != 0x3f3f3f3f; } int dfs(int x, int t, int fl){ if(x == t){ return fl; } vis[x] = 1; int rs = fl; for(int i = now[x]; i && rs; i = nx[i]){ int y = eg[i], z = ln[i], c = cs[i]; if(!vis[y] && z && dis[y] == dis[x] + c){ int k = dfs(y, t, min(rs, z)); if(!k){ dis[y] = inf; } ln[i] -= k; ln[i^1] += k; rs -= k; mc += k * c; } } return fl - rs; } int dinic(int s, int t){ int mf = 0, tmp; while(spfa(s, t)){ while(tmp = dfs(s, t, inf)){ mf += tmp; } } return mf; } int main(){ scanf("%d%d%d%d", &n, &m, &s, &t); int S = n + 1, T = n + 2; for(int i = 1; i <= m; ++ i){ int u, v, w, c; scanf("%d%d%d%d", &u, &v, &w, &c); if(c > 0){ add(u, v, w, c); add(v, u, 0, -c); } else { nd[u] -= w; nd[v] += w; add(v, u, w, -c); add(u, v, 0, c); mc += c * w; } } int tag = tot; for(int i = 1; i <= n; ++ i){ if(nd[i] > 0){ add(S, i, nd[i], 0); add(i, S, 0, 0); } else { add(i, T, -nd[i], 0); add(T, i, 0, 0); } } add(t, s, inf, 0); add(s, t, 0, 0); dinic(S, T); mf += ln[tot]; for(int i = tag + 1; i <= tot; ++ i){ ln[i] = 0; } mf += dinic(s, t); printf("%d %d\n", mf, mc); return 0; }
19. PKUSC2024 - 分流器
题意转化为至少多少次操作使得每个点都被操作偶数次。不妨模拟
点击查看代码
//qoj8671 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 50010; int n, ch[N][2], mn = 10000000; void pr(int x){ static int a[N]; memset(a, 0, sizeof(a)); int len = 1; a[1] = 1; while(true){ if(x >= 8){ for(int i = 1; i <= len; ++ i){ a[i] *= (1 << 8); } for(int i = 1; i <= len; ++ i){ a[i+1] += a[i] / 10; a[i] %= 10; } while(a[len+1]){ ++ len; a[len+1] += a[len] / 10; a[len] %= 10; } x -= 8; } else { for(int i = 1; i <= len; ++ i){ a[i] *= (1 << x); } for(int i = 1; i <= len; ++ i){ a[i+1] += a[i] / 10; a[i] %= 10; } while(a[len+1]){ ++ len; a[len+1] += a[len] / 10; a[len] %= 10; } break; } } for(int i = len; i >= 1; -- i){ putchar(a[i]+'0'); } } const ll P = (1ll << 50); struct bigint{ ll a[1010]; int qmn(){ for(int i = 0; i <= 1000; ++ i){ a[i+1] += a[i] / P; a[i] %= P; } for(int i = 0; i <= 1000; ++ i){ if(a[i]){ for(ll j = 0; j < 50; ++ j){ if(a[i] & (1ll << j)){ return i * 50 + j; } } } } } void dv2(){ for(int i = 1000; i >= 0; -- i){ if(a[i] & 1){ a[i-1] += P; } a[i] >>= 1; } } } a[N]; int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d%d", &ch[i][0], &ch[i][1]); } a[1].a[1000] |= (1 << 10); for(int i = 1; i <= n; ++ i){ mn = min(mn, a[i].qmn()); a[i].dv2(); for(int j = 0; j <= 1000; ++ j){ a[ch[i][0]].a[j] += a[i].a[j]; a[ch[i][1]].a[j] += a[i].a[j]; } } mn = min(mn, a[n+1].qmn()); pr(50011 - mn); return 0; }
20. LGP6114 - 【模板】Lyndon 分解
定义一个串是 lyndon 串:
这个串严格最小后缀是它本身。
这个串是它所有循环同构的串中字典序严格最小的。
两种定义等价,证明:
设串
定义一推出定义二:
定义二推出定义一:
定义一个串的 lyndon 分解
均为 lyndon 串且 。
可以证明这样的分解存在且唯一:
引理:若
是 lyndon 串且 ,则 是 lyndon 串。
证明:若
存在性:由引理,可以初始化每个 lyndon 子串为单字符,然后合并直至
唯一性:若
引理:若
为一个 lyndon 串前缀, ,则有 为 lyndon 串。
证明
线性求 lyndon 分解的 Duval 算法:
维护
表示已经分解了 的字符。 ( 为 lyndon 串, ), 。
(from wucstdio)
考虑
- 若
,则继续匹配; - 若
,则由引理得 为一个 lyndon 串,合并为一个 ; - 若
,则固定 分为 个 lyndon 串,从 开头处重新开始分解。
点击查看代码
// Problem: P6114 【模板】Lyndon 分解 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P6114 // Memory Limit: 500 MB // Time Limit: 300000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e6 + 10; int n, ans; char s[N]; int main(){ scanf("%s", s+1); n = strlen(s+1); for(int i = 1; i <= n;){ int j = i, k = i+1; while(k <= n && s[j] <= s[k]){ if(s[j] < s[k]){ j = i; } else { ++ j; } ++ k; } while(i <= j){ ans ^= i + (k - j - 1); i += k - j; } } printf("%d\n", ans); return 0; }
21. LGP1368 - 【模板】最小表示法
首先对串
点击查看代码
// Problem: P1368 【模板】最小表示法 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P1368 // Memory Limit: 250 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 6e5 + 10; int n, a[N], r[N], tot; int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); a[i+n] = a[i]; } for(int i = 1; i <= n + n;){ int j = i, k = i + 1; while(k <= n + n && a[j] <= a[k]){ if(a[j] < a[k]){ j = i; } else { ++ j; } ++ k; } while(i <= j){ r[++tot] = i+k-j-1; i += k-j; } } for(int i = 1; i <= n+n; ++ i){ if(r[i] >= n){ for(int j = r[i-1]+1, k = 1; k <= n; ++ j, ++ k){ printf("%d ", a[j]); } puts(""); return 0; } } }
22. some string problem
题意:给一个串
可以发现一定有一个最优解是把每个
23. CTT2012 - 楼房重建
经典 trick:线段树维护前缀最大值数量。
考虑一栋楼可以被看见当且仅当
考虑线段树每个节点维护区间最大值
两个区间合并时:
直接合并; 可用左子树答案加上右子树在左子树 影响下的答案(记作 )。
接下来实现
- 若
,则右子树答案不会因 改变,返回 ( 定义是右子树答案); - 否则左子树不会有贡献,返回
。
点击查看代码
//qoj3679 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; typedef long long ll; int n, m, a[N]; struct node{ int cnt, id; } t[N*4]; bool geq(int x, int y){ if(!y){ return a[x]; } return ((ll)a[x] * y > (ll)a[y] * x); } void build(int p, int l, int r){ t[p].cnt = 1; t[p].id = l; if(l != r){ int mid = l + r >> 1; build(p<<1, l, mid); build(p<<1|1, mid+1, r); } } int calc(int p, int l, int r, int h){ if(l == r){ return geq(l, h); } else { int mid = l + r >> 1; if(geq(t[p<<1].id, h)){ return calc(p<<1, l, mid, h) + t[p].cnt; } else { return calc(p<<1|1, mid+1, r, h); } } } void mdf(int p, int l, int r, int x){ if(l != r){ int mid = l + r >> 1; if(x <= mid){ mdf(p<<1, l, mid, x); } else { mdf(p<<1|1, mid+1, r, x); } t[p].id = geq(t[p<<1|1].id, t[p<<1].id) ? t[p<<1|1].id : t[p<<1].id; t[p].cnt = calc(p<<1|1, mid+1, r, t[p<<1].id); } } int main(){ scanf("%d%d", &n, &m); build(1, 1, n); for(int i = 1; i <= m; ++ i){ int x, y; scanf("%d%d", &x, &y); a[x] = y; mdf(1, 1, n, x); printf("%d\n", calc(1, 1, n, 0)); } return 0; }
24. CTT2012 - 序列染色
设
同理可求得
点击查看代码
//qoj3680 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 10; const ll P = 1e9 + 7; int n, k; ll f[N], g[N], F[N], G[N]; char s[N]; #define cl(x) ((x) == 'X' ? 2 : 1) int main(){ scanf("%d%d", &n, &k); scanf("%s", s+1); f[0] = g[n+1] = 1; for(int i = 1, la = 0; i <= n; ++ i){ if(s[i] == 'W'){ la = i; } if(la >= i - k + 1){ f[i] = f[i-1] * cl(s[i]) % P; } else { int v = s[i-k] == 'B' ? 0 : i==k ? 1 : f[i-k-1]; f[i] = (P + f[i-1] * cl(s[i]) - v) % P; } } for(int i = n; i >= 1; -- i){ f[i] = s[i] == 'B' ? 0 : f[i-1]; } for(int i = 1, la = 0; i <= n; ++ i){ if(s[i] == 'W'){ la = i; } if(la < i - k + 1){ F[i] = f[i-k]; } } for(int i = n, la = n+1; i >= 1; -- i){ if(s[i] == 'B'){ la = i; } if(la <= i + k - 1){ g[i] = g[i+1] * cl(s[i]) % P; } else { int v = s[i+k] == 'W' ? 0 : n-i+1==k ? 1 : g[i+k+1]; g[i] = (P + g[i+1] * cl(s[i]) - v) % P; } } for(int i = 1; i <= n; ++ i){ g[i] = s[i] == 'W' ? 0 : g[i+1]; } for(int i = n, la = n+1; i >= 1; -- i){ if(s[i] == 'B'){ la = i; } if(la > i + k - 1){ G[i] = g[i+k]; } } ll ans = 0, sum = 0; for(int i = 1; i <= n; ++ i){ sum = (sum * cl(s[i]) + F[i]) % P; ans = (ans + sum * G[i+1]) % P; } printf("%lld\n", ans); return 0; }
25. CTT2012 - 模积和
考虑
拆完后答案是:
点击查看代码
//qoj3681 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 19940417, i2 = 9970209, i6 = 3323403; ll n, m; ll sm(ll x){ x %= P; return x * (x+1) % P * i2 % P; } ll sm(ll l, ll r){ return (sm(r) - sm(l-1) + P) % P; } ll pw(ll x){ x %= P; return x * (x+1) % P * (x+x+1) % P * i6 % P; } ll pw(ll l, ll r){ return (pw(r) - pw(l-1) + P) % P; } ll clc(ll x){ ll ans = 0; for(ll l = 1, r; l <= x; l = r + 1){ r = x / (x / l); ans = (ans + (x / l) % P * sm(l, r)) % P; } return ans; } ll calc(ll x, ll y){ ll ans = 0; for(ll l = 1, r; l <= min(x, y); l = r + 1){ r = min(x / (x / l), y / (y / l)); ans += x * y % P * (r-l+1) % P; ans -= y * (x / l) % P * sm(l, r) % P; ans -= x * (y / l) % P * sm(l, r) % P; ans += (x / l) * (y / l) % P * pw(l, r) % P; ans = (ans % P + P) % P; } return ans; } int main(){ scanf("%lld%lld", &n, &m); ll ans = 0; ans += (n * n % P + P - clc(n)) * (m * m % P + P - clc(m)) % P; ans -= calc(n, m); printf("%lld\n", (ans % P + P) % P); return 0; }
26. NOI2023 - 桂花树
条件等价于
于是考虑一个一个点加入,设现在树上共有
- 将
插入到一条边上/一个点上,方案数 ; - 在一条边上插入一个编号
的点,并将 作为它的子节点,方案数 ; - 作为先前插入过的点,方案数
。
维护
这样是不重不漏的。
点击查看代码
//qoj6756 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 3010, P = 1e9 + 7; int c, T, n, m, k, f[1<<10], g[1<<10]; void upd(int &x, int y){ x += y; if(x >= P){ x -= P; } } int main(){ scanf("%d%d", &c, &T); while(T--){ scanf("%d%d%d", &n, &m, &k); for(int i = 1; i < n; ++ i){ scanf("%d", &c); } g[0] = 1; for(int i = 1; i <= m; ++ i){ memset(f, 0, sizeof(f)); for(int s = 0; s < (1 << k); ++ s){ if(!g[s]){ continue; } int cnt = n + i - 1 + __builtin_popcount(s); if(s & 1){ upd(f[s>>1], g[s]); } else { upd(f[s>>1], (ll)g[s] * (cnt + cnt - 1) % P); for(int j = 0; j < k; ++ j){ if(!(((s >> 1) >> j) & 1)){ int t = (s >> 1) ^ (1 << j); upd(f[t], (ll)g[s] * (cnt - 1) % P); } } } } memcpy(g, f, sizeof(g)); } printf("%lld\n", g[0]); memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g)); } return 0; }
27. NOI2023 - 方格染色
考虑把斜线与直线/斜线与斜线的交暴力算出来后直接扫描线即可。
如何算交:
- 对于截距相同的斜线,若有相交则合并(需多次判断);
- 判断斜线与每条直线的交点,去重后从答案中减去。
然后直接扫描线即可,需要离散化。
点击查看代码
//qoj6755 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5 + 10; int n, m, Q, q; struct rec{ int x, y, xx, yy; } qr[N]; vector<rec> cur; ll ans = 0; int bx[N], by[N], bc; struct scanline{ int l, r, val; }; vector<scanline> g[N]; struct node{ int tag, mn, mv, vl; } t[N*4]; void build(int p, int l, int r){ t[p].vl = t[p].mv = bx[r+1] - bx[l]; if(l != r){ 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 v){ if(qr < l || r < ql){ return; } else if(ql <= l && r <= qr){ t[p].mn += v; t[p].tag += v; } else { int mid = l + r >> 1; 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; add(p<<1, l, mid, ql, qr, v); add(p<<1|1, mid+1, r, ql, qr, v); if(t[p<<1].mn == t[p<<1|1].mn){ t[p].mn = t[p<<1].mn; t[p].mv = t[p<<1].mv + t[p<<1|1].mv; } else if(t[p<<1].mn < t[p<<1|1].mn){ t[p].mn = t[p<<1].mn; t[p].mv = t[p<<1].mv; } else { t[p].mn = t[p<<1|1].mn; t[p].mv = t[p<<1|1].mv; } } } void smx(){ if(!q){ return; } for(int i = 1; i <= q; ++ i){ ++ qr[i].xx; ++ qr[i].yy; bx[bc+1] = qr[i].x; bx[bc+2] = qr[i].xx; by[bc+1] = qr[i].y; by[bc+2] = qr[i].yy; bc += 2; } sort(bx + 1, bx + bc + 1); int mx = unique(bx + 1, bx + bc + 1) - bx - 1; sort(by + 1, by + bc + 1); int my = unique(by + 1, by + bc + 1) - by - 1; for(int i = 1; i <= q; ++ i){ qr[i].x = lower_bound(bx + 1, bx + mx + 1, qr[i].x) - bx; qr[i].xx = lower_bound(bx + 1, bx + mx + 1, qr[i].xx) - bx; qr[i].y = lower_bound(by + 1, by + my + 1, qr[i].y) - by; qr[i].yy = lower_bound(by + 1, by + my + 1, qr[i].yy) - by; g[qr[i].y].push_back({qr[i].x, qr[i].xx, 1}); g[qr[i].yy].push_back({qr[i].x, qr[i].xx, -1}); } build(1, 1, mx-1); for(int i = 1; i < my; ++ i){ for(auto p : g[i]){ add(1, 1, mx-1, p.l, p.r-1, p.val); } ans += 1ll * (by[i+1] - by[i]) * (bx[mx] - bx[1] - (t[1].mn ? 0 : t[1].mv)); } } int main(){ scanf("%d", &n); scanf("%d%d%d", &n, &m, &Q); for(int i = 1; i <= Q; ++ i){ int op, x, y, xx, yy; scanf("%d%d%d%d%d", &op, &x, &y, &xx, &yy); if(x > xx){ swap(x, xx); } if(y > yy){ swap(y, yy); } if(op != 3){ qr[++q] = {x, y, xx, yy}; } else { cur.push_back({x, y, xx, yy}); } } int k = cur.size(); for(int p = 0; p < k; ++ p){ for(int i = 0; i < k; ++ i){ for(int j = 0; j < k; ++ j){ if(cur[i].x == -1 || cur[j].x == -1 || i == j){ continue; } if(cur[i].y - cur[i].x != cur[j].y - cur[j].x){ continue; } if(cur[i].x > cur[j].xx || cur[j].x > cur[i].xx){ continue; } cur[i].x = min(cur[i].x, cur[j].x); cur[i].y = min(cur[i].y, cur[j].y); cur[i].xx = max(cur[i].xx, cur[j].xx); cur[i].yy = max(cur[i].yy, cur[j].yy); cur[j].x = -1; } } } for(int i = 0; i < k; ++ i){ if(cur[i].x == -1){ continue; } map<int, int> mp; mp.clear(); int cnt = 0, b = cur[i].y - cur[i].x; for(int j = 1; j <= q; ++ j){ if(qr[j].x == qr[j].xx){ int x = qr[j].x, y = qr[j].x + b; if(x >= cur[i].x && x <= cur[i].xx && y >= qr[j].y && y <= qr[j].yy && !mp[x]){ mp[x] = 1; ++ cnt; } } else { int x = qr[j].y - b, y = qr[j].y; if(y >= cur[i].y && y <= cur[i].yy && x >= qr[j].x && x <= qr[j].xx && !mp[x]){ mp[x] = 1; ++ cnt; } } } ans += cur[i].xx - cur[i].x + 1 - cnt; } smx(); printf("%lld\n", ans); return 0; }
28. APIO2016 - Boat
考虑一个
发现
考虑设
于是就可以利用这个来 dp。设
本质不同的
点击查看代码
//qoj2 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll P = 1e9 + 7; const int N = 510, M = 1010; int n, a[N], b[N], pl[M]; ll f[N][M], g[N][M], h[N][M]; ll fac[N], inv[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; } int main(){ scanf("%d", &n); fac[0] = 1; for(int i = 1; i <= n; ++ i){ fac[i] = fac[i-1] * i % P; scanf("%d%d", &a[i], &b[i]); pl[i] = a[i]; pl[i+n] = b[i] + 1; } inv[n] = qp(fac[n], P-2); for(int i = n-1; i >= 0; -- i){ inv[i] = inv[i+1] * (i+1) % P; } sort(pl + 1, pl + n + n + 1); int m = unique(pl + 1, pl + n + n + 1) - pl - 1; for(int i = 1; i <= n; ++ i){ a[i] = lower_bound(pl + 1, pl + m + 1, a[i]) - pl; b[i] = lower_bound(pl + 1, pl + m + 1, b[i] + 1) - pl - 1; } f[0][0] = 1; for(int j = 0; j < m; ++ j){ g[0][j] = 1; } for(int j = 1; j < m; ++ j){ h[0][j] = 1; ll val = 1; for(int i = 1; i <= n; ++ i){ val = val * (pl[j+1] - pl[j] + i - 1) % P; h[i][j] = val * inv[i] % P; } } for(int i = 1; i <= n; ++ i){ for(int j = 1; j < m; ++ j){ if(j < a[i] || j > b[i]){ continue; } int cnt = 0; for(int k = i; k >= 1; -- k){ if(a[k] <= j && j <= b[k]){ ++ cnt; } f[i][j] = (f[i][j] + g[k-1][j-1] * h[cnt][j]) % P; } } for(int j = 1; j <= m; ++ j){ g[i][j] = (g[i][j-1] + f[i][j]) % P; } } ll ans = 0; for(int i = 1; i <= n; ++ i){ for(int j = 1; j < m; ++ j){ ans = (ans + f[i][j]) % P; } } printf("%lld\n", ans); return 0; }
29. APIO2016 - Fireworks
设
原因是
那么转移在图像上形如:
- 将
的地方全部换为斜率为 的直线; - 将
斜率为 的直线向右平移 ; - 在
之间插入斜率为 的直线。
考虑维护每个转折点的横坐标;若相邻斜率差
- 弹出最大的
个横坐标(其中 表示 的儿子个数); - 弹出最大的
个横坐标, 后插入回去。
然后两个函数合并则直接将维护横坐标的堆合并即可。使用左偏树维护。
最后求答案时,由于
点击查看代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 6e5 + 10; int n, m, f[N], v[N], ind[N]; ll sum; int rt[N], top; struct node{ int ls, rs, ds; ll x; } t[N]; int mg(int x, int y){ if(!x || !y){ return x + y; } if(t[x].x < t[y].x || (t[x].x == t[y].x && x < y)){ swap(x, y); } t[x].rs = mg(t[x].rs, y); if(t[t[x].ls].ds < t[t[x].rs].ds){ swap(t[x].ls, t[x].rs); } t[x].ds = t[t[x].rs].ds + 1; return x; } int pop(int x){ return mg(t[x].ls, t[x].rs); } int main(){ scanf("%d%d", &n, &m); for(int i = 2; i <= n + m; ++ i){ scanf("%d%d", &f[i], &v[i]); ++ ind[f[i]]; sum += v[i]; } for(int i = n + m; i > 1; -- i){ ll x = 0, y = 0; if(i <= n){ for(int j = 1; j < ind[i]; ++ j){ rt[i] = pop(rt[i]); } y = t[rt[i]].x; rt[i] = pop(rt[i]); x = t[rt[i]].x; rt[i] = pop(rt[i]); } t[++top].x = x + v[i]; t[++top].x = y + v[i]; rt[i] = mg(rt[i], mg(top-1, top)); rt[f[i]] = mg(rt[f[i]], rt[i]); } for(int j = 1; j < ind[1]; ++ j){ rt[1] = pop(rt[1]); } rt[1] = pop(rt[1]); while(rt[1]){ sum -= t[rt[1]].x; rt[1] = pop(rt[1]); } printf("%lld\n", sum); return 0; }
30. APIO2016 - Gap
Sub1:
询问
Sub2:
首先询问
点击查看代码
//qoj4 #include <bits/stdc++.h> using namespace std; #include "gap.h" long long findGap(int T, int n){ static long long a[200010]; memset(a, 0, sizeof(a)); if(T == 1){ a[0] = -3, a[n+1] = 2e18; for(int i = 1, j = n; i <= j; ++ i, -- j){ MinMax(a[i-1] + 1, a[j+1] - 1, &a[i], &a[j]); } long long mx = 0; for(int i = 1; i < n; ++ i){ mx = max(mx, a[i+1] - a[i]); } return mx; } else { long long mn, mx; MinMax(-3, 2e18, &mn, &mx); long long k = (mx - mn - 1) / (n - 1) + 1; int cnt = 0; if(mx - mn <= 1){ return mx - mn; } a[++cnt] = mn; a[++cnt] = mx; ++ mn; -- mx; for(long long i = mn; i <= mx; i += k){ long long tx, ty; MinMax(i, i+k-1, &tx, &ty); a[++cnt] = tx; a[++cnt] = ty; } sort(a + 1, a + cnt + 1); mx = 0; for(int i = 1; i < cnt; ++ i){ mx = max(mx, a[i+1] - a[i]); } return mx; } }
31. CF713C - Sonya and Problem Wihtout a Legend
首先令
维护函数
点击查看代码
// Problem: Sonya and Problem Wihtout a Legend // Contest: Luogu // URL: https://www.luogu.com.cn/problem/CF713C // Memory Limit: 250 MB // Time Limit: 5000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; typedef long long ll; int n, a; ll ans = 0; priority_queue<int> q; int main(){ int T = 1; while(T--){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d", &a); a -= i; ans += a; q.push(a); q.push(a); q.pop(); } while(!q.empty()){ ans -= q.top(); q.pop(); } printf("%lld\n", ans); } return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步