随机化+二维差分(区间更新) 7.21牛客暑期多校训练营二 J

10.

White Rabbit has a rectangular farmland of n*m. In each of the grid there is a kind of plant. The plant inthe j-th column of the i-th row belongs the ai-th type.White Cloud wants to help White Rabbit fertilize plants, but the i-th plant can only adapt to the i-th fertilizer.If the j-th fertilizer is applied to the i-th plant (i!=j), the plant will immediately die.Now White Cloud plans to apply fertilizers T times. In the i-th plan, White Cloud will use k[i]-th fertilizer tofertilize all the plants in a rectangle x1[i]...x2[i]...y2[i]].White rabbits wants to know how many plants would eventually die if they were to be fertilized according tothe expected schedule of White Cloud.

输入描述:The first line of input contains 3 integers n,m,T(n*m<=1000000,T<=1000000)For the next n lines, each line contains m integers in range[1,n*m] denoting the type of plant in each grid.For the next T lines, the i-th line contains 5 integersx1,y1,x2,y2,k(1<=x1<=x2<=n,1<=y1<=y2<=m,1<=k<=n*m)

输出描述:Print an integer, denoting the number of plants which would die.

示例1:

输入2 2 21 22 31 1 2 2 22 1 2 1 1

输出3

题目大意:

​ 有一个n*m的矩形,每个位置有一个数。有T次操作,每次往一个子矩形的每个格子中放入一个数。求有多少个格子中被放入了至少一个与对应位置不相同的数。n*m<=1e6,T<=1e6

想法:

​ 比赛的时候没看过这题,看到讨论区题解有人说可以用随机化算法。搞懂后感觉很神奇,特此记录。

思路:

​ 1、随机化输入值。如果每次植物浇的都是同值的肥料,那么最后的值会是原值的整数倍,植物不死。反之,植物死了。但是如果植物值是1呢,或者两次肥料值相加刚好是整数倍呢?这种取巧就不行了。所以要随机化输入值,把植物原值映射到1e6以上。random_shuffle(mp+1,mp+1+n*m);

​ 2、二维差分。植物改变值该怎么实现?矩阵大小n*m<=1e6,改变次数T<=1e6,暴力一个个改过来肯定超时。https://www.cnblogs.com/OIerShawnZhou/p/7348088.html 二维差分登场。用它来实现区间更新。

​ 3、辅助数组next的前缀和即改变总量。还有cnt前缀和为改变总次数 ,那么求答0案ans就是if(now[i][j]!=1LL*cnt[i][j]*v[i][j]) ans++;

 

标准题解

1、先考虑一个特殊的情况:矩形中的数和T次操作放的数都为0或1。对于这种情况,我们只需要用矩阵前缀和统计一下每个格子被多少个0覆盖,被多少个1覆盖。如果一个格子的数为0且被放入了至少一个1或这个格子的数位1且被放入了至少一个0则就会对答案产生贡献。然后考虑原问题。如果某个格子的数是i,而它被放入了至少一个j,且i!=j,则需要统计进入答案。注意到,i!=j则i和j至少有一个二进制位不相同。我们枚举0~19的每一个二进制位,然后把所有数字按照这一位是0还是1划分成两个合,就变成了上述特殊情况的问题。一个格子只要至少在某一个二进制位的子问题时被统计进入答案,就加到总答案中去。复杂度 (nm+T)log(nm)

2、我们给每种植物花费随机一个[1e6,1e7]内的权值,然后对于每种植物,求覆盖它的所有化肥的权值之和。如果这个值是这个格子权值的倍数,则有很大概率只有同种化肥覆盖了当前格子,否则没有。然后我们可以尽量只随机质数(所以权值只弄到1e7内,可以预处理所有质数)。这样,如果一个格子权值是p,那么正确率就是(p-1)/p 如果多做几次就几乎没有问题了。

 

AC代码:

#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define mem(a,b) memset((a),(b),sizeof(a))
#define MP make_pair
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define all(x) x.begin(),x.end()
#define _GLIBCXX_PERMIT_BACKWARD_HASH
#include <ext/hash_map>
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef vector<int> VI;
typedef vector<ll> VL;
struct str_hash{size_t operator()(const string& str)const{return __stl_hash_string(str.c_str());}};
const int INF=0x3f3f3f3f;
const ll LLINF=0x3f3f3f3f3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-4;
const int MAX=1e6+10;
const ll mod=1e9+7;
/****************************************  head  ****************************************/
VI v[MAX],cnt[MAX];
VL now[MAX];
int mp[MAX];
int main()
{
    for(i=1;i<=n*m;i++) mp[i]=i;
    random_shuffle(mp+1,mp+1+n*m);    //给每个数生成随机数 
    int n,m,q,i,j,x1,x2,y1,y2,vv;
    while(~scanf("%d%d%d",&n,&m,&q))
    {
​
        for(i=0;i<=n+1;i++)                 //分配大小 
        {
            v[i].resize(m+5);            
            cnt[i].resize(m+5);
            now[i].resize(m+5);
        }
        for(i=1;i<=n;i++)                    
        {
            for(j=1;j<=m;j++)
            {
                scanf("%d",&v[i][j]);
                v[i][j]=mp[v[i][j]];        //随机化矩阵内的数 
                now[i][j]=0;
                cnt[i][j]=0;
            }
        }
         
        for(i=1;i<=n;i++)                    
        {
            for(j=1;j<=m;j++)
            {
                cout << mp[v[i][j]]<<endl;
            }
        }
        
        while(q--)                                      //用了二维差分,替代两层循环更新矩阵状态 
        {
            scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&vv);
            now[x1][y1]+=mp[vv];                //改变量 
            now[x1][y2+1]-=mp[vv];
            now[x2+1][y1]-=mp[vv];
            now[x2+1][y2+1]+=mp[vv];
            cnt[x1][y1]++;                      //改变次数
            cnt[x1][y2+1]--;
            cnt[x2+1][y1]--;
            cnt[x2+1][y2+1]++;
        }
        int ans=0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                now[i][j]+=now[i-1][j]+now[i][j-1]-now[i-1][j-1];     //前缀和为改变总量 
                cnt[i][j]+=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1];   //前缀和为改变总次数 
                if(now[i][j]!=1LL*cnt[i][j]*v[i][j]) ans++;         //若改变后的值不是原值的整数倍,说明不是浇了同值多次的肥料,植物死了,答案+1 
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

参考:

http://tokitsukaze.live/2018/07/21/2018niuke2.J/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值