P6100 [USACO19FEB]Painting the Barn G 題解
阿新 • • 發佈:2021-08-09
這道題首先考慮把求面積轉化成求滿足條件的格點數量,接著考慮怎麼做。因為這兩個矩形沒有交,所以一個點最多隻會被覆蓋一次。於是最終答案中 \(K\) 層塗料的只可能是初始下 \(K-1\) 層塗料(被塗一層)和 \(K\) 層塗料(沒有被塗)。
於是就考慮轉化,塗了 \(K-1\) 層的格子標記為 \(1\),即塗了這個格子能是答案加 \(1\);塗了 \(K\) 層塗料的格子標記為 \(-1\),因為塗了這個格子會使答案減少 \(1\),最終的答案就是雙子矩陣最大和,這可以看作是雙子序列最大和的延續和拓展。
考慮兩個子矩陣的相對位置:
第三種可以看作是前兩種情況的公共部分。
對於前兩種情況,我們可以列舉分割線,求出答案。對於分割線的兩邊,我們可以 \(4\)
具體看程式碼(自認為實現非常清晰):
#include<bits/stdc++.h> #define log(a) cerr<<"\033[32m[DEBUG] "<<#a<<'='<<(a)<<" @ line "<<__LINE__<<"\033[0m"<<endl #define LL long long #define SZ(x) ((int)x.size()-1) #define ms(a,b) memset(a,b,sizeof a) #define F(i,a,b) for(int i=(a);i<=(b);++i) #define DF(i,a,b) for(int i=(a);i>=(b);--i) using namespace std; inline int read(){char ch=getchar(); int w=1,c=0; for(;!isdigit(ch);ch=getchar()) if (ch=='-') w=-1; for(;isdigit(ch);ch=getchar()) c=(c<<1)+(c<<3)+(ch^48); return w*c; } const int N=210; int f[N][N],lf[N][N][N],rf[N][N][N],uf[N][N][N],df[N][N][N],s1[N][N],s2[N][N],lmaxn[N],rmaxn[N],umaxn[N],dmaxn[N],ans,s; //l表示left(左),r表示right(右),u表示up(上),d表示down(下) //f[i][j]表示這個格子的值(使用字首和快速處理) //l/r/u/df[i][j][k]表示上界為i,下界為j,處理到k //l/r/u/dmaxn[i]表示字首/字尾求max到i //s1[i][j]表示f[1][j]~f[i][j]的和 //s2[i][j]表示f[i][1]~f[i][j]的和 signed main(){ int n=read(),k=read(); F(i,1,n){ int kx=read()+1,ky=read()+1,tx=read(),ty=read();//轉換為割點數:kx,ky加上1,讓每個點表示它左下角的格子 f[kx][ky]++;f[kx][ty+1]--;f[tx+1][ky]--;f[tx+1][ty+1]++;//差分 } F(i,1,200) F(j,1,200){ f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1];//字首和即為值 int t=0;//如果不是k或k-1就為0 if(f[i][j]==k)t=-1,s++;//處理矩陣元素值 if(f[i][j]+1==k)t=1;//處理矩陣元素值 s1[i][j]=s1[i-1][j]+t;//求字首和 s2[i][j]=s2[i][j-1]+t;//求字首和 } F(i,1,200) F(j,i,200){ F(k,1,200){ lf[i][j][k]=max(lf[i][j][k-1],0)+s1[j][k]-s1[i-1][k];//dp(跟不跟左邊相連) lmaxn[k]=max(lmaxn[k],lf[i][j][k]);//求這一列為右邊界,左邊的最大值 } DF(k,200,1){ rf[i][j][k]=max(rf[i][j][k+1],0)+s1[j][k]-s1[i-1][k];//dp(跟不跟右邊相連) rmaxn[k]=max(rmaxn[k],rf[i][j][k]);//求這一列為左邊界,右邊的最大值 } F(k,1,200){ uf[i][j][k]=max(uf[i][j][k-1],0)+s2[k][j]-s2[k][i-1];//dp(跟不跟上邊相連) umaxn[k]=max(umaxn[k],uf[i][j][k]);//求這一行為下邊界,上面的最大值 } DF(k,200,1){ df[i][j][k]=max(df[i][j][k+1],0)+s2[k][j]-s2[k][i-1];//dp(跟不跟下邊相連) dmaxn[k]=max(dmaxn[k],df[i][j][k]);//求這一行為上邊界,下面的最大值 } } F(i,1,200)lmaxn[i]=max(lmaxn[i-1],lmaxn[i]);//字首max DF(i,200,1)rmaxn[i]=max(rmaxn[i+1],rmaxn[i]);//字首max F(i,1,200)umaxn[i]=max(umaxn[i-1],umaxn[i]);//字首max DF(i,200,1)dmaxn[i]=max(dmaxn[i+1],dmaxn[i]);//字首max F(i,1,200)ans=max(ans,max(lmaxn[i]+rmaxn[i+1],umaxn[i]+dmaxn[i+1]));//列舉分界線求值 cout<<s+ans;//主要加上初始值 return 0; }