省选动态规划专题
消失之物
发现背包顺序无关,那么就支持
code
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #define rep(i,s,t,p) for(int i = s;i <= t; i += p) #define drep(i,s,t,p) for(int i = s;i >= t; i -= p) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 2e3 + 10; int n,m,f[N],a[N],g[N]; signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); cin>>n>>m;rep(i,1,n,1) cin>>a[i]; f[0] = 1; rep(i,1,n,1) drep(j,m,a[i],1) f[j] = (f[j] + f[j - a[i]])%10; rep(i,1,n,1){ g[0] = 1; rep(j,1,m,1){ if(j - a[i] >= 0) g[j] = (f[j] - g[j - a[i]] + 10) % 10; else g[j] = f[j] % 10; cout<<g[j]; } cout<<'\n'; } }
[国家集训队] 墨墨的等式
同余最短路板子,见省选图论专题。
[BalticOI 2022 Day1] Uplifting Excursion
首先,贪心地想,肯定是将所有的都选上,如果比
对于剩下的一点,考虑背包填,剩下的这个背包的值域是
剩下的就是一个多重背包,二进制优化即可。时间复杂度
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; #define int ll int m,l;unordered_map<int,int> a,b; int f[300010],siz; void DP(int w,int v,int c){ if(w > 0){ for(int s = 1;c > 0;c -= s,s <<= 1){ int k = min(s,c); drep(i,siz,k*w,1) f[i] = max(f[i],f[i-k*w]+k*v); } } else{ for(int s = 1;c > 0;c -= s,s <<= 1){ int k = min(s,c); rep(i,0,siz + k*w,1) f[i] = max(f[i],f[i-k*w]+k*v); } } } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cin>>m>>l;int cbmn = 0,cbmx = 0,sum = 0; rep(i,-m,m,1){ cin>>a[i],b[i] = a[i];sum += a[i]*i; if(i < 0) cbmn += a[i]*i;else cbmx += a[i]*i; } if(l > cbmx || l < cbmn) return puts("impossible"),0; if(sum > l){ drep(i,m,1,1){ if(sum <= l) break; int res = sum - l,num = min(res/i,a[i]); b[i] -= num;sum -= num*i; } } else{ rep(i,-m,-1,1){ if(sum >= l) break; int res = sum - l,num = min(res/i,a[i]); b[i] -= num;sum -= num*i; } } memset(f,-0x3f,sizeof f); int res = m*m-l+sum;f[res] = 0;siz = m*m*2; rep(i,-m,m,1) f[res] += b[i]; rep(i,0,2*m,1){ if(i == m) continue; if(b[i-m]) DP(-(i-m),-1,min(b[i-m],siz)); if(a[i-m] - b[i-m]) DP(i-m,1,min(a[i-m]-b[i-m],siz)); } if(f[m*m] < 0) cout<<"impossible\n"; else cout<<f[m*m]<<'\n'; }
[JSOI2016] 最佳团体
01分数规划+树上背包,简单说一下01分数规划吧,因为之前没怎么见过。
01分数规划
给定
考虑二分,假设当前二分的答案为
对于本题
就是01分数规划套树上背包,注意你的背包可能写假了,写成
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; const int N = 2510; #define eb emplace_back int k,n,p[N],s[N],siz[N],dfn[N],rdfn[N],tim; vector<int> e[N]; db a[N],f[N][N]; void dfs(int x){ siz[x] = 1; if(x) rdfn[dfn[x] = ++tim] = x; for(auto y:e[x]) dfs(y),siz[x] += siz[y]; } bool check(db mid){ a[0] = 0; rep(i,1,n,1) a[i] = p[i] - s[i]*mid; rep(i,1,n+1,1) rep(j,1,k+1,1) f[i][j] = -1e10; drep(i,n,1,1) rep(j,1,k,1) f[i][j] = max(f[i+1][j-1] + a[rdfn[i]],f[i + siz[rdfn[i]]][j]); return f[1][k] > 0; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cin>>k>>n; rep(i,1,n,1){int f;cin>>s[i]>>p[i]>>f,e[f].eb(i);} dfs(0); db l = 0,r = 3,ans = 0; while(l + 1e-4 <= r){ db mid = (l+r)/2; if(check(mid)) ans = mid,l = mid; else r = mid; } cout<<fixed<<setprecision(3); cout<<ans<<'\n'; }
[POI2015] MYJ
容易证明,每个点的值为
由于
答案显然为
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; const int N = 60,M = 4010; int n,m,a[M],b[M],c[M],sum[N][N]; int f[N][N][M],ans[N];pair<int,int> lst[N][N][M]; vector<int> num; void dfs(int l,int r,int s){ if(l > r) return; pair<int,int> it = lst[l][r][s]; ans[it.second] = num[it.first]; dfs(l,it.second-1,it.first);dfs(it.second+1,r,it.first); } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); num.emplace_back(-1); cin>>n>>m;rep(i,1,m,1) cin>>a[i]>>b[i]>>c[i],num.emplace_back(c[i]); sort(num.begin(),num.end());num.erase(unique(num.begin(),num.end()),num.end()); rep(i,1,m,1) c[i] = lower_bound(num.begin(),num.end(),c[i]) - num.begin(); drep(i,m,0,1){ rep(j,1,m,1) if(c[j] == i) rep(l,1,a[j],1) rep(r,b[j],n,1) sum[l][r]++; rep(len,1,n,1) rep(l,1,n-len+1,1){ int r = l + len - 1; f[l][r][i] = f[l][r][i + 1],lst[l][r][i] = lst[l][r][i+1]; rep(p,l,r,1){ int s = sum[l][r] - sum[l][p-1] - sum[p+1][r]; int v = (f[l][p-1][i]+f[p+1][r][i]+s*num[i]); if(v > f[l][r][i]) f[l][r][i] = v,lst[l][r][i] = {i,p}; } if(!lst[l][r][i].first) lst[l][r][i] = {i,l}; } } cout<<f[1][n][1]<<'\n';dfs(1,n,1); rep(i,1,n,1) cout<<ans[i]<<' '; }
[SBCOI2020] 一直在你身旁
打表发现这玩意没有决策单调性。这个
那么就可以二分找这个分界点了?但是发现二分出这个分界点没有意义啊,最后转移还是
发现当
记
对于
时间复杂度
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; const int N = 7110; int n,a[N]; ll f[N][N]; signed main(){ cin.tie(nullptr)->sync_with_stdio(false); int T;cin>>T; while(T--){ cin>>n;rep(i,1,n,1) cin>>a[i]; rep(i,1,n,1) rep(j,1,n,1) f[i][j] = 0; rep(r,2,n,1){ int k = r;deque<int> q;q.push_back(r); drep(l,r-1,1,1){ if(r - l == 1){f[l][r] = a[l];continue;} while(k > l && f[l][k-1] >= f[k][r]) k--; f[l][r] = f[l][k] + a[k]; while(q.size() && q.front() >= k) q.pop_front(); if(q.size()) f[l][r] = min(f[l][r],f[q.front()+1][r] + a[q.front()]); while(q.size() && f[q.back() + 1][r] + a[q.back()] >= f[l + 1][r] + a[l]) q.pop_back(); q.push_back(l); } } cout<<f[1][n]<<'\n'; } }
Yet Another Minimization Problem
决策单调性优化dp,但只有分治能做。
状态设计是显然的,设
但如果暴力求
总时间复杂度
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; const int N = 1e5 + 10; int n,k,a[N],now,ct[N],l,r; ll f[21][N],sum; ll calc(int L,int R){ while(l > L) sum += ct[a[--l]],ct[a[l]]++; while(r < R) sum += ct[a[++r]],ct[a[r]]++; while(l < L) ct[a[l]]--,sum -= ct[a[l++]]; while(r > R) ct[a[r]]--,sum -= ct[a[r--]]; return sum; } void work(int l,int r,int L,int R){ if(l > r) return; int mid = (l + r) >> 1,pos = 0; ll res = 0x3f3f3f3f3f3f3f3f,sum = 0; drep(i,min(R,mid),L,1){ ll cjs = f[now][i-1]; cjs += calc(i,mid); if(cjs < res) res = cjs,pos = i; } f[now+1][mid] = res; work(l,mid-1,L,pos);work(mid+1,r,pos,R); } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cin>>n>>k;rep(i,1,n,1) cin>>a[i]; memset(f,0x3f,sizeof f);f[0][0] = 0; rep(j,1,k,1){ l = 1,r = 0,sum = 0; memset(ct,0,sizeof ct); now = j - 1; work(1,n,1,n); } cout<<f[k][n]<<'\n'; }
[APIO2014] 序列分割
斜率优化板子,但要注意小的那一维放在前面,和内存访问连续有关。
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; namespace IO{ #define gc getchar_unlocked #define pc putchar_unlocked template<class T> inline void read(T &x){ x = 0;char s = gc(); for(;s < '0' || s > '9';s = gc()); for(;'0' <= s && s <= '9';s = gc()) x = (x << 1) + (x << 3) + (s ^ 48); } template<class T,class... Args> inline void read(T &x,Args&... argc){read(x);read(argc...);} inline void write(char x){pc(x);} template<class T> inline void write(T x){ static int sta[30],top = 0; do sta[++top] = x%10;while(x /= 10); while(top) pc(sta[top--]+'0'); } template<class T,class... Args> inline void write(T x,Args... argc){write(x);write(argc...);} }using IO::read;using IO::write; const int N = 1e5 + 10,K = 210; int n,k,a[N],q[N],l,r,pre[K][N]; ll f[K][N],s[N]; bool vis[N];int cjs; ll pow(ll x){return x*x;} ll get(int p,int k){return pow(s[p]) - f[k][p];} db slope(int j,int k,int p){ if(s[j] == s[k]) return 1e12; return 1.0*(get(j,p)-get(k,p))/(s[j] - s[k]); } void print(int k,int x){ if(!k) return; print(k-1,pre[k][x]); if(pre[k][x]) cjs++,write(pre[k][x],' '),vis[pre[k][x]] = true; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); read(n,k);rep(i,1,n,1) read(a[i]),s[i] = s[i-1] + a[i]; rep(num,1,k,1){ l = 1,r = 0;q[++r] = 1; rep(i,2,n,1){ while(l < r && slope(q[l+1],q[l],num-1) <= s[i]) l++; int j = q[l]; f[num][i] = f[num-1][j] + s[i]*s[j] - pow(s[j]); pre[num][i] = j; while(l < r && slope(q[r],q[r-1],num-1) >= slope(i,q[r],num-1)) r--; q[++r] = i; } } write(f[k][n],'\n'); print(k,n); rep(i,1,n,1){ if(cjs >= k) break; if(vis[i]) continue; write(i,' ');cjs++; } }
Sonya and Problem Wihtout a Legend
slope trick 维护dp,但是发现
code
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 1e6 + 10; int n,a[N],b[N]; inline void solve(){ cin>>n; priority_queue<int> q;ll ans = 0; for(int i = 1;i <= n; ++i){ cin>>a[i];b[i] = (a[i] -= i); q.push(a[i]); if(q.top() > a[i]){ ans += q.top() - a[i]; q.pop();q.push(a[i]); } a[i] = q.top(); } cout<<ans<<'\n'; // for(int i = n - 1;i >= 1; --i) a[i] = min(a[i],a[i + 1]); // for(int i = 1;i <= n; ++i) cout<<a[i]+i<<' '; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
Yet Another Partiton Problem
神仙题,为了做这个特意去学的凸包。凸包启发式合并+斜率优化+可持久化李超线段树。
显然的dp方程
时间复杂度
建议凸包用斜率判断,更好写,也不容易写错。
code
#include<bits/stdc++.h> using namespace std; #define rep(i,s,t,p) for(int i = s;i <= t;i += p) #define drep(i,s,t,p) for(int i = s;i >= t;i -= p) #ifdef LOCAL auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout); #else auto I = stdin,O = stdout; #endif using ll = long long;using ull = unsigned long long; using db = long double;using ldb = long double; const int N = 2e4 + 10,K = 110; int n,k,f[N],g[N],a[N],sta[N],top; struct Line{ int k,b; Line(){b = 2e9;k = 0;} Line(int K,int B):k(K),b(B){} ll get(int x){return k*x+b;} }s[N]; ll get(int p,int x){return s[p].get(x);} struct LCST{ struct node{int ls,rs,v;}t[N*18]; #define ls(x) t[x].ls #define rs(x) t[x].rs #define v(x) t[x].v int tot = 0,rt[N]; void upd(int pre,int &k,int l,int r,int p){ k = ++tot;t[k] = t[pre]; if(l == r){ if(get(p,l) < get(v(k),l)) v(k) = p; return; } int mid = (l + r) >> 1; if(get(p,mid) < get(v(k),mid)) swap(v(k),p); if(get(p,l) < get(v(k),l)) upd(ls(pre),ls(k),l,mid,p); if(get(p,r) < get(v(k),r)) upd(rs(pre),rs(k),mid+1,r,p); } int qry(int k,int l,int r,int x){ if(!k) return 2e9; if(l == r) return get(v(k),x); int mid = (l + r) >> 1,res = get(v(k),x); return min(res,x <= mid?qry(ls(k),l,mid,x):qry(rs(k),mid+1,r,x)); } #undef ls #undef rs #undef v }sgt; struct Point{ int x,y; Point(){} Point(int X,int Y):x(X),y(Y){} db slope(const Point &a){return 1.0*(y-a.y)/(x-a.x);} }; struct Convex_Hull{ deque<Point> ch; void Join(Convex_Hull &x){ int t = x.ch.size()-1; // ch.front().x > x.ch.back().x if(ch.size() <= x.ch.size()){//ch 并入 x.ch for(auto p:ch){ while(t && x.ch[t-1].slope(x.ch[t]) >= x.ch[t-1].slope(p)) t--,x.ch.pop_back(); x.ch.emplace_back(p);t++; } ch.swap(x.ch); } else{ int sz = ch.size() - 1; for(Point p;~t;--t){ p = x.ch[t]; while(sz && ch[1].slope(ch[0]) <= ch[1].slope(p)) ch.pop_front(),sz--; ch.emplace_front(p);sz++; } } } int qry(int k){ int l = 1,r = ch.size() - 1,ans = 0; while(l <= r){ int mid = (l + r) >> 1; if(ch[mid].slope(ch[mid-1]) < k) l = mid + 1,ans = mid; else r = mid - 1; } return ch[ans].y - (ch[ans].x - 1)*k; } }ch[N]; signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cin>>n>>k;rep(i,1,n,1) cin>>a[i]; rep(i,1,n,1) f[i] = max(a[i],f[i-1]); rep(i,1,n,1) f[i] *= i; rep(j,2,k,1){ rep(i,1,n,1) swap(f[i],g[i]); sgt.tot = 0;top = 0; // cerr<<"\033[32m";cerr<<j<<":::\n";cerr<<"\033[0m"; rep(i,1,n,1){ // cerr<<i<<":\n"; ch[i].ch.resize(1);ch[i].ch[0] = Point(i,g[i-1]); while(top && a[sta[top]] < a[i]) ch[i].Join(ch[sta[top--]]); s[i].k = a[i]; s[i].b = ch[i].qry(a[i]); sgt.upd(sgt.rt[sta[top]],sgt.rt[i],1,n,i); f[i] = sgt.qry(sgt.rt[i],1,n,i); // cerr<<"This is Line : "; // cerr<<s[i].k<<' '<<s[i].b<<'\n'; // cerr<<"This is convex hull: "; // for(auto q:ch[i].ch) cerr<<q.x<<' '<<q.y<<" | "; // cerr<<'\n'; // cerr<<'\n'; sta[++top] = i; } // cerr<<'\n'; } cout<<f[n]<<'\n'; }
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18638479
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】