牛客网多校2 farm(巧妙的随机数hash,前缀和/二维树状数组)

题目:给出一个N*M的矩阵药田,药田中每个格子都种有一种编号的为1~N*M的药,随后一个T表示有T次浇水操作,之后先是给出N行M列的矩阵(药田),再给出T行,每行有5个数,X1,Y1,X2,Y2和W,表示这次操作会在左上角为(X1,Y1),右下角为(X2,Y2)的矩阵中浇W这种药水,若是该某个药格中的药编号与被浇到的药水不同,则死亡,问你T次浇水操作后整个药田死了多少颗药?

思路:这里对每个药的编号进行随机hash得到一个大于最大编号1e6的数值,之后每次浇水操作,我们通过使用二维树状数组区间更新的巧妙办法,在已知更新的矩阵左上角和右下角情况下update四个点的值即可在O(logn*logm)时间内实现树状数组的区间更新(需要注意的是这里更新的值也是药对应药水的hash值才行).那么最终想要知道某个格子的药是否存活只需要判断他的树状数组的值%本身的hash值是否为0即可,在随机数足够优秀的条件下,能整除的情况说明该药格每次浇到的药水的是合适的(或者没有浇到任何药水),那么它是存活的,反之死亡。想法很奇妙,思路来自https://blog.csdn.net/weixin_41156591/article/details/81150890

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
int n,m,q;
vector<ll>a[maxn],sum[maxn];
ll has[maxn];
void init()
{
    srand(time(NULL));
    for(int i=1;i<maxn-5;i++)
        has[i]=(ll)rand()*1e6+(ll)rand();//随机数hash
}
int lowbit(int x){return x&-x;}
void update(int x,int y,ll val)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        sum[i][j]+=val;
}
ll query(int x,int y)
{
    ll ans=0;
    for(int i=x;i>0;i-=lowbit(i))
    for(int j=y;j>0;j-=lowbit(j))
        ans+=sum[i][j];
    return ans;
}
int main()
{
    init();
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        a[i].push_back(0);
        sum[i].push_back(0);
        for(int j=1;j<=m;j++)
        {
            int x;
            scanf("%d",&x);
            a[i].push_back(has[x]);
            sum[i].push_back(0);
        }
    }
    while(q--)
    {
        int x1,y1,x2,y2;
        int color;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&color);
        update(x1,y1,has[color]);
        update(x1,y2+1,-has[color]);
        update(x2+1,y1,-has[color]);
        update(x2+1,y2+1,has[color]);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(query(i,j)%a[i][j]!=0)//判断是不是有其它的药
                ans++;
        }
    printf("%d\n",ans);
    return 0;
}

但是,为什么要用树状数组维护呢?

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
int n,m,q;
vector<ll>a[maxn],sum[maxn];
ll has[maxn];
void init()
{
    srand(time(NULL));
    for(int i=1;i<maxn-5;i++)
        has[i]=(ll)rand()*1e6+(ll)rand();
}
void update(int x,int y,ll val){sum[x][y]+=val;}
int main()
{
    init();
    scanf("%d%d%d",&n,&m,&q);
    for(int i=0;i<=m;i++)//多放点0
    {
        sum[0].push_back(0);
        sum[n+1].push_back(0);
    }
    for(int i=1;i<=n;i++)
    {
        a[i].push_back(0);
        sum[i].push_back(0);
        for(int j=1;j<=m;j++)
        {
            int x;
            scanf("%d",&x);
            a[i].push_back(has[x]);
            sum[i].push_back(0);
        }
    }
    for(int i=0;i<=n+1;i++)
        sum[i].push_back(0);//再多放几个0
    while(q--)
    {
        int x1,y1,x2,y2;
        int color;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&color);
        update(x1,y1,has[color]);
        update(x1,y2+1,-has[color]);
        update(x2+1,y1,-has[color]);
        update(x2+1,y2+1,has[color]);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];//
            if(sum[i][j]%a[i][j]!=0)
                ans++;
        }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/81154719