1. 程式人生 > 其它 >P6100 [USACO19FEB]Painting the Barn G 題解

P6100 [USACO19FEB]Painting the Barn G 題解

這道題首先考慮把求面積轉化成求滿足條件的格點數量,接著考慮怎麼做。因為這兩個矩形沒有交,所以一個點最多隻會被覆蓋一次。於是最終答案中 \(K\) 層塗料的只可能是初始下 \(K-1\) 層塗料(被塗一層)和 \(K\) 層塗料(沒有被塗)。

於是就考慮轉化,塗了 \(K-1\) 層的格子標記為 \(1\),即塗了這個格子能是答案加 \(1\);塗了 \(K\) 層塗料的格子標記為 \(-1\),因為塗了這個格子會使答案減少 \(1\),最終的答案就是雙子矩陣最大和,這可以看作是雙子序列最大和的延續和拓展。

考慮兩個子矩陣的相對位置:

第三種可以看作是前兩種情況的公共部分。

對於前兩種情況,我們可以列舉分割線,求出答案。對於分割線的兩邊,我們可以 \(4\)

個方向都做 \(dp\),列舉兩個邊界去求。最後記得字首 \(\max\) 一下,因為不一定一定要貼著分割線,判斷沒有即對 \(0\)\(\max\) 即可。

具體看程式碼(自認為實現非常清晰):

#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;
}