CF771E Bear and Rectangle Strips
阿新 • • 發佈:2021-07-11
一、題目
二、解法
首先設計出暴力 \(dp\),設 \(dp[i][j]\) 表示第一行考慮到 \(i\),第二行考慮到 \(j\) 的最大得分,先寫轉移:
- 擴充套件第一行,可以無得分讓 \(i+1\);可以選從 \(i+1\) 開始的和為 \(0\) 的段(選右端點最小的)
- 擴充套件第二行,可以無得分讓 \(j+1\),可以選從 \(j+1\) 開始的和為 \(0\) 的段。
- \(i=j\) 時,同時擴充套件兩行,選一個 \(i+1\) 開始的何為 \(0\) 的矩陣。
優化的關鍵是隻有 \(i=j\) 時兩行的轉移才有交叉,否則兩行的轉移是相對獨立的。一個至關重要的 \(\tt observation\)
那麼我們以 \(dp[i][i]\) 為轉移主體,也就是考慮兩行前 \(i\) 列的最大得分,對於 \(i\not=j\) 的擴充套件,我們只需要找到最小的 \(j\) 使得 \(dp[i][j]=dp[i][i]+1\)(或者 \(dp[j][i]=dp[i][i]+1\)),因為如果 \(dp[i][j]>dp[i][i]+1\) 那麼說明某一行擴充套件多了,不符合上述結論所以自然不用考慮。
轉移的時候維護那個 \(j\) 即可,時間複雜度 \(O(n)\)
三、總結
考慮有效轉移是優化的重要方法,也就是如果某一種轉移的作用會在以後被解決那麼我們就不用考慮它。
#include <cstdio> #include <vector> #include <iostream> #include <map> using namespace std; const int M = 300005; #define int long long int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,a[2][M],nx[3][M],s[3],dp[M];map<int,int> mp[3]; struct node { int x,y,c; };vector<node> vc[M]; void add(int x,int y,int c) { dp[max(x,y)]=max(dp[max(x,y)],c); vc[min(x,y)].push_back(node{x,y,c}); } void extend(int x,int y,int c) { if(x<n) { add(x+1,y,c); int i=nx[0][x+1]; if(i) add(i,y,c+1); } if(y<n) { add(x,y+1,c); int i=nx[1][y+1]; if(i) add(x,i,c+1); } if(x<n && x==y) { int i=nx[2][x+1]; if(i) add(i,i,c+1); } } signed main() { n=read(); for(int i=0;i<2;i++) for(int j=1;j<=n;j++) a[i][j]=read(); for(int i=1;i<=n;i++) { for(int j=0;j<3;j++) mp[j][s[j]]=i; s[0]+=a[0][i]; s[1]+=a[1][i]; s[2]+=a[0][i]+a[1][i]; for(int j=0;j<3;j++) if(mp[j][s[j]]) nx[j][mp[j][s[j]]]=i; } for(int i=0;i<n;i++) { extend(i,i,dp[i]); int l=n+1,r=n+1; for(node a:vc[i]) { if(a.y==i && a.c==dp[i]+1) l=min(l,a.x); if(a.x==i && a.c==dp[i]+1) r=min(r,a.y); } if(l<=n) extend(l,i,dp[i]+1); if(r<=n) extend(i,r,dp[i]+1); } printf("%lld\n",dp[n]); }