二维区间的一些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;
} 
View Code

牛客的评测不知道是什么原因,如果把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;
 } 
View Code

 

二维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;
} 
View Code

 

posted @ 2020-03-22 23:55  Y-KnightQin  阅读(360)  评论(0编辑  收藏  举报