二维区间的一些sao做法
1. 与数据结构相关
最典型的就是树套树了,这里的例题是二维线段树.
牛客2020 CCPC Wannafly Winter Camp Day5 Div.1&2 I Practice for KD Tree
找二维区间最大值
这里需要注意的是,一开始题目要进行区间加的操作,所以我们要用到二维前缀和的思想
可以这么记住:上行、右列先拓展,左下右上+,左上右下-。
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} const int maxn = 2e3+10; const int INF = 0x3f3f3f3f; int n,m1,m2,i,j; ll maxx[maxn<<2][maxn<<2],a[maxn][maxn],v; void add(int x1,int y1,int x2,int y2,ll value) { a[x1][y1]+=v; a[x2+1][y2+1]+=v; a[x2+1][y1]-=v; a[x1][y2+1]-=v; } void updatey(int l,int r,int num,int x,int y,ll value) { maxx[x][num]=max(maxx[x][num],value); if (l==r) return; int mid=(l+r)>>1; if (y<=mid) updatey(l,mid,num*2,x,y,value); else updatey(mid+1,r,num*2+1,x,y,value); } void updatex(int l,int r,int num,int x,int y,ll value) { updatey(1,n,1,num,y,value); if (l==r) return; int mid=(l+r)>>1; if (x<=mid) updatex(l,mid,num*2,x,y,value); else updatex(mid+1,r,num*2+1,x,y,value); } ll queryy(int l,int r,int num,int limitt_y1,int limitt_y2,int xroot) { if (limitt_y1<=l && r<=limitt_y2) { return maxx[xroot][num]; } ll ans=0; int mid=(l+r)>>1; if (limitt_y1<=mid) ans=max(ans,queryy(l,mid,num*2,limitt_y1,limitt_y2,xroot)); if (limitt_y2>mid) ans=max(ans,queryy(mid+1,r,num*2+1,limitt_y1,limitt_y2,xroot)); return ans; } ll queryx(int l,int r,int num,int limitt_x1,int limitt_x2,int limitt_y1,int limitt_y2) { if (limitt_x1<=l && r<=limitt_x2) { return queryy(1,n,1,limitt_y1,limitt_y2,num); } ll ans=0; int mid=(l+r)>>1; if (limitt_x1<=mid) ans=max(ans,queryx(l,mid,num*2,limitt_x1,limitt_x2,limitt_y1,limitt_y2)); if (limitt_x2>mid) ans=max(ans,queryx(mid+1,r,num*2+1,limitt_x1,limitt_x2,limitt_y1,limitt_y2)); return ans; } int main() { n=read(); m1=read(); m2=read(); int x1,y1,x2,y2; for (i=1;i<=m1;i++) { scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&v); add(x1,y1,x2,y2,v); } for (i=1;i<=n;i++) for (j=1;j<=n;j++) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]; for (i=1;i<=n;i++) for (j=1;j<=n;j++) updatex(1,n,1,i,j,a[i][j]); while (m2--) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); printf("%lld\n",queryx(1,n,1,x1,x2,y1,y2)); } return 0; }
牛客的评测不知道是什么原因,如果把x1,y1,x2,y2设在代码最开头那里的话,就会CE,而把它们放在主函数里面就不会......
2. 与算法相关
记忆化dp
codeforces 1199F
题意就是要把整个矩阵涂白,然后求最小代价。
我们设dp[x1][y1][x2][y2],(x1,y1)为左下坐标,(x2,y2)为右上坐标,表示这个矩阵涂白的最小代价
可以用拼接的方法来进行转移,对于当前矩阵,我们可以通过拼接上下两个长相等的矩阵得到,也可以拼接左右两个宽相等的矩阵得到。
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; inline int read(){int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} const int maxn = 55; const int INF = 0x3f3f3f3f; int n,i,f[maxn][maxn][maxn][maxn]; char s[maxn][maxn]; int solve(int x1,int y1,int x2,int y2) { if (f[x1][y1][x2][y2]!=-1) return f[x1][y1][x2][y2]; if (x1==x2 && y1==y2) { if (s[x1][y1]=='#') f[x1][y1][x2][y2]=1; else f[x1][y1][x2][y2]=0; return f[x1][y1][x2][y2]; } f[x1][y1][x2][y2]=max(x2-x1+1,y2-y1+1); for (int i=x1;i<x2;i++) { if (f[x1][y1][i][y2]==-1) solve(x1,y1,i,y2); if (f[i+1][y1][x2][y2]==-1) solve(i+1,y1,x2,y2); f[x1][y1][x2][y2]=min(f[x1][y1][x2][y2],f[x1][y1][i][y2]+f[i+1][y1][x2][y2]); } for (int i=y1;i<y2;i++) { if (f[x1][i+1][x2][y2]==-1) solve(x1,i+1,x2,y2); if (f[x1][y1][x2][i]==-1) solve(x1,y1,x2,i); f[x1][y1][x2][y2]=min(f[x1][y1][x2][y2],f[x1][i+1][x2][y2]+f[x1][y1][x2][i]); } return f[x1][y1][x2][y2]; } int main() { n=read(); for (i=1;i<=n;i++) { scanf("%s",s[i]+1); } memset(f,-1,sizeof(f)); cout<<solve(1,1,n,n); return 0; }
二维RMQ
就是上面那题二维线段树变成RMQ的做法(过肯定是过不了的,内存肯定会爆,知道做法就行)
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} const int maxn = 2e3+1; const int INF = 0x3f3f3f3f; int n,m1,m2,i,j; ll maxx[maxn][maxn][11][11],a[maxn][maxn],v; void add(int x1,int y1,int x2,int y2,ll value) { a[x1][y1]+=v; a[x2+1][y2+1]+=v; a[x2+1][y1]-=v; a[x1][y2+1]-=v; } void init() { int i,j,k,p; for (i=1;i<=n;i++) for (j=1;j<=n;j++) maxx[i][j][0][0]=a[i][j]; // pre[2]=pre[3]=1; // for (i=4;i<=n;i++) pre[i]=pre[i>>1]+1; // int up1,up2=pre[n]+1; int up1=(int)log2(n); int up2=(int)log2(n); for (i=0;i<=up1;i++) for (j=0;j<=up2;j++) { // cout<<1<<endl; if (!i && !j) continue; for (k=1;(k+(1<<i)-1)<=n;k++) for (p=1;(p+(1<<j)-1)<=n;p++) { if (i==0) maxx[k][p][i][j]=max(maxx[k][p][i][j-1],maxx[k][p+(1<<(j-1))][i][j-1]); else maxx[k][p][i][j]=max(maxx[k][p][i-1][j],maxx[k+(1<<(i-1))][p][i-1][j]); } } } ll query(int x1,int y1,int x2,int y2) { ll ans=-INF; int p=(int)log2((x2-x1+1)); int q=(int)log2((y2-y1+1)); ans=max(maxx[x1][y1][p][q],maxx[x1][y2-(1<<q)+1][p][q]); ans=max(ans,max(maxx[x2-(1<<p)+1][y1][p][q],maxx[x2-(1<<p)+1][y2-(1<<q)+1][p][q])); return ans; } int main() { n=read(); m1=read(); m2=read(); int x1,y1,x2,y2; for (i=1;i<=m1;i++) { scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&v); add(x1,y1,x2,y2,v); } for (i=1;i<=n;i++) for (j=1;j<=n;j++) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1]; init(); while (m2--) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); printf("%lld\n",query(x1,y1,x2,y2)); } return 0; }