stream pack 1.
收录动态规划题
1. LuoguP11189 -「KDOI-10」水杯降温
tag:二分、状态设计;link。
发现只用操作
; 。
设
猜测
对于叶子,
计算左端点后即可二分查找右端点。
点击查看代码
//P11189 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; typedef long long ll; const ll inf = 5e18; int c, T, n, lf[N]; vector<int> g[N]; ll a[N], b[N], le[N], ri[N]; bool chk(int x, ll v, int fa){ ll mn = 0, mx = 0; for(int i : g[x]){ if(i == fa){ continue; } if(ri[i] < v){ return 0; } mn += max(le[i], v); mx += ri[i]; mx = min(mx, inf); } ll val = b[x] + v; return val >= mn && val <= mx; } void dfs(int x, int fa){ if(lf[x]){ le[x] = max(0ll, -b[x]); ri[x] = 1e18; return; } for(int i : g[x]){ if(i != fa){ dfs(i, x); if(le[i] > ri[i]){ le[x] = 1; ri[x] = 0; return; } } } ll sum = 0; for(int i : g[x]){ if(i != fa){ sum += le[i]; } } le[x] = max(0ll, sum - b[x]); ll L = le[x]-1, R = 1.1e13; while(L < R){ ll mid = L + R + 1 >> 1; if(chk(x, mid, fa)){ L = mid; } else { R = mid - 1; } } ri[x] = L; } int main(){ scanf("%d%d", &c, &T); while(T--){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ lf[i] = 1; vector<int> ().swap(g[i]); } for(int i = 2; i <= n; ++ i){ int fa; scanf("%d", &fa); lf[fa] = 0; g[fa].push_back(i); } for(int i = 1; i <= n; ++ i){ scanf("%lld", &a[i]); } for(int i = 1; i <= n; ++ i){ b[i] = a[i]; for(int j : g[i]){ b[i] -= a[j]; } } dfs(1, 0); if(le[1] <= ri[1]){ puts("Huoyu"); } else { puts("Shuiniao"); } } return 0; }
2. qoj365/JOISC2017 - 鉄道旅行 (Railway Trip)
tag:倍增、正确性证明;link。
容易发现一个结论:假设一条路径
所以重新建图:对点
倍增,设
所以询问时从
点击查看代码
//qoj365 #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int n, k, q, a[N], le[N][20], ri[N][20]; int st[N], tp; int main(){ scanf("%d%d%d", &n, &k, &q); st[tp=0] = 1; for(int i = 1; i <= n; ++ i){ scanf("%d", &a[i]); while(tp && a[st[tp]] < a[i]) -- tp; le[i][0] = st[tp]; st[++tp] = i; } st[tp=0] = n; for(int i = n; i >= 1; -- i){ while(tp && a[st[tp]] < a[i]) -- tp; ri[i][0] = st[tp]; st[++tp] = i; } for(int i = 1; i <= 19; ++ i){ for(int j = 1; j <= n; ++ j){ le[j][i] = min(le[le[j][i-1]][i-1], le[ri[j][i-1]][i-1]); ri[j][i] = max(ri[ri[j][i-1]][i-1], ri[le[j][i-1]][i-1]); } } while(q--){ int x, y, ans = 0; scanf("%d%d", &x, &y); if(x > y) swap(x, y); int p = x, q = x; for(int i = 19; i >= 0; -- i){ int pp = min(le[p][i], le[q][i]); int qq = max(ri[p][i], ri[q][i]); if(qq < y){ p = pp; q = qq; ans += 1 << i; } } x = q; p = y, q = y; for(int i = 19; i >= 0; -- i){ int pp = min(le[p][i], le[q][i]); int qq = max(ri[p][i], ri[q][i]); if(pp > x){ p = pp; q = qq; ans += 1 << i; } } printf("%d\n", ans); } return 0; }
3. 2023Xi'anR - Random Variables
tag:容斥;link。
设
考虑容斥,设
左边表示在未填的第一个位置中填入一个未被选定的数;右边表示在未填的第一个位置以及后面任意
最后答案即为
点击查看代码
//qoj9254 #include <bits/stdc++.h> using namespace std; const int N = 1e3 + 10; typedef long long ll; int T, n; ll m, P, f[N][N], C[N][N], ans; struct Mod{ ll m, p; void init(int pp){ m = ((__int128)1 << 64) / pp; p = pp; } ll operator ()(ll x){ if(p == 2){ return x & 1; } return x - ((__int128(x) * m) >> 64) * p; } } mod; ll qp(ll x, ll y){ ll ans = 1; while(y){ if(y & 1){ ans = mod(ans * x); } x = mod(x * x); y >>= 1; } return ans; } int main(){ scanf("%d%lld", &T, &P); n = 1000; mod.init(P); C[0][0] = 1; for(int i = 1; i <= n; ++ i){ C[i][0] = 1; for(int j = 1; j <= i; ++ j){ C[i][j] = C[i-1][j] + C[i-1][j-1]; if(C[i][j] >= P){ C[i][j] -= P; } } } while(T--){ ans = 0; scanf("%d%lld", &n, &m); for(int k = 1; k < n; ++ k){ f[0][0] = 1; for(int i = 1; i <= n; ++ i){ f[i][i/(k+1)+1] = 0; for(int j = 0; j * (k + 1) <= i; ++ j){ f[i][j] = mod(f[i-1][j] * (m - j)); if(i >= k + 1 && j){ ll tmp = mod(f[i-k-1][j-1] * (m - j + 1)); tmp = mod(tmp * C[n-(i-k)][k]); f[i][j] -= tmp; if(f[i][j] < 0){ f[i][j] += P; } } } } for(int j = 0; j * (k + 1) <= n; ++ j){ ans += f[n][j]; f[n][j] = 0; if(ans >= P){ ans -= P; } } } printf("%lld\n", mod(- ans + P + n * qp(m, n)) % P); } return 0; }
4. APIO2024 - Train
考虑按时间顺序 dp,设
其中
容易发现有
二分队列算法步骤:
- 计算队首两个决策
的交点是否 ,如果是则弹出队首; - 若存在队首,用队首更新此时的答案;
- 计算队尾两个决策
的交点是否 队尾决策 与当前决策的交点,如果是则弹出队尾; - 插入当前决策。
- 可以使用第二个队列维护队内交点减少算法常数。
- 使用二分求解两个决策交点,具体的,求出第一个下标使得前者劣于后者。
点击查看代码
//qoj8725 #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<int> vint; #ifndef ONLINE_JUDGE #include "train.h" #endif const int N = 1e5 + 10; int n, m, w, pl[N*4], tp; basic_string<int> eat[N*4], que[N], qcr[N]; basic_string<pair<int, int> > tra[N*4]; int lp[N], rp[N], mc[N]; unordered_map<int, ll> f[N]; unordered_map<int, int> vs[N]; struct SegTree{ int rt[N*4], tot = 1; struct node{ int sum, ls, rs; } t[N*100]; void add(int &p, int l, int r, int x){ ++tot; t[tot] = t[p]; p = tot; if(l == r){ ++ t[p].sum; } else { int mid = l + r >> 1; if(x <= mid){ add(t[p].ls, l, mid, x); } else { add(t[p].rs, mid+1, r, x); } t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum; } } int ask(int p, int l, int r, int ql, int qr){ if(qr < l || r < ql || !p){ return 0; } else if(ql <= l && r <= qr){ return t[p].sum; } else { int mid = l + r >> 1; return ask(t[p].ls, l, mid, ql, qr) + ask(t[p].rs, mid+1, r, ql, qr); } } ll ask(int l, int r){ return ask(rt[r-1], 1, tp, l+1, tp); } } T; ll fx(int x, int p, int val){ return f[x][p] + mc[x] * T.ask(p, val); } int crs(int x, int p, int q){ int l = q, r = tp + 1; while(l < r){ int mid = l + r >> 1; if(fx(x, p, mid) < fx(x, q, mid)){ l = mid + 1; } else { r = mid; } } return l; } ll solve(int N, int M, int W, vint mcc, vint xx, vint yy, vint st, vint ed, vint lc, vint le, vint ri){ n = N; m = M; w = W; for(int i = 0; i < n; ++ i){ mc[i] = mcc[i]; lp[i] = 1; rp[i] = 0; que[i].push_back(0); qcr[i].push_back(0); } for(int i : st){ pl[++tp] = i; } for(int i : ed){ pl[++tp] = i; } for(int i : le){ pl[++tp] = i; } for(int i : ri){ pl[++tp] = i; } sort(pl + 1, pl + tp + 1); tp = unique(pl + 1, pl + tp + 1) - pl - 1; for(int i = 0; i < m; ++ i){ st[i] = lower_bound(pl + 1, pl + tp + 1, st[i]) - pl; ed[i] = lower_bound(pl + 1, pl + tp + 1, ed[i]) - pl; tra[st[i]].push_back(make_pair(-i-1, xx[i])); tra[ed[i]].push_back(make_pair(i+1, yy[i])); } for(int i = 0; i < w; ++ i){ le[i] = lower_bound(pl + 1, pl + tp + 1, le[i]) - pl; ri[i] = lower_bound(pl + 1, pl + tp + 1, ri[i]) - pl; eat[ri[i]].push_back(le[i]); } for(int i = 1; i <= tp; ++ i){ T.rt[i] = T.rt[i-1]; for(int j : eat[i]){ T.add(T.rt[i], 1, tp, j); } } f[0][0] = 0; que[0].push_back(0); qcr[0].push_back(0); que[0][++rp[0]] = 0; for(int i = 1; i <= tp; ++ i){ sort(tra[i].begin(), tra[i].end()); for(auto j : tra[i]){ int id = j.first, pos = j.second; if(id < 0){ id = -id - 1; ll val = 1e18; while(lp[pos] < rp[pos] && qcr[pos][lp[pos]] <= i){ ++ lp[pos]; } if(lp[pos] > rp[pos]){ continue; } int p = que[pos][lp[pos]]; val = f[pos][p] + mc[pos] * T.ask(p, i); if(f[pos].find(i) != f[pos].end()){ f[pos][i] = min(f[pos][i], val); } else { f[pos][i] = val; } } else { -- id; if(f[xx[id]].find(st[id]) == f[xx[id]].end()){ continue; } ll val = f[xx[id]][st[id]] + lc[id]; if(f[pos].find(i) != f[pos].end()){ f[pos][i] = min(f[pos][i], val); } else { f[pos][i] = val; } while(lp[pos] < rp[pos] && qcr[pos][rp[pos]-1] >= crs(pos, que[pos][rp[pos]], i)){ -- rp[pos]; } que[pos].push_back(0); qcr[pos].push_back(0); que[pos][++rp[pos]] = i; if(lp[pos] < rp[pos]){ qcr[pos][rp[pos]-1] = crs(pos, que[pos][rp[pos]-1], que[pos][rp[pos]]); } } } } ll val = 1e18; for(auto i : f[n-1]){ val = min(val, i.second + 1ll * mc[n-1] * T.ask(i.first, tp+1)); } if(val > 1e17){ return -1; } return val; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步