这道题的做法很多。
二维前缀和+随机
用两个差分二维数组,一个存种类和,另一个存总次数,对于每一次撒农药操作(x1,y1,x2,y2,k),将种类和数组的对应区域加上k,同时将总次数对应区域加上1。(关于差分二维数组,这里不多做解释。
然后,将两个差分数组做个二维的前缀和,得到每个位置的种类和与总次数。
若某位置的 种类*总次数!=种类和,则说明有别的农药,则该位置植物死亡。
但是,如果种类是初始的[1,n*m],则可能导致 k1+k2+...+kn == ki * n,从而出现误判,因此,需要给每个种类一个随机的代号,即我们使用的其实并不是初始的种类,而是该种类的代号,这样误判的几率就会极小。
时间复杂度O(n*m)
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (root<<1)
#define rs ((root<<1)|1)
const int M=1e6+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e7;
struct FastIO//输入挂
{static const int S=200;int wpos;char wbuf[S];FastIO():wpos(0){}inline int xchar(){static char buf[S];static int len=0,pos=0;if(pos==len) pos=0,len=fread(buf,1,S,stdin);if(pos==len) exit(0);return buf[pos++];}inline int read(){int s=1,c=xchar(),x=0;while(c<=32) c=xchar();if(c=='-') s=-1,c=xchar();for(;'0'<=c&&c<='9';c=xchar()) x=x*10+c-'0';return x*s;}~FastIO(){if(wpos) fwrite(wbuf,1,wpos,stdout),wpos=0;}
}io;
int t,n,m;
int ran[M];
void init(){srand(time(0));For(i,1,M-5) ran[i]=rand()%mod+M;
}
int getid(int x,int y){if(x-1<0||y-1<0) return 0;return (x-1)*m+y;
}
int v[M],v2[M];
ll v1[M];void update(int id[],int z){v1[id[0]]+=z;v1[id[1]]+=z;v1[id[2]]-=z;v1[id[3]]-=z;
}
void update1(int id[],int z){v2[id[0]]+=z;v2[id[1]]+=z;v2[id[2]]-=z;v2[id[3]]-=z;
}
int main(){init();
// scanf("%d %d %d",&n,&m,&t);n=io.read(),m=io.read(),t=io.read();For(i,1,n){For(j,1,m){v[getid(i,j)]=io.read();//scanf("%d",&v[getid(i,j)]);}}int x,y,xx,yy,z,id[4];For(i,1,t){x=io.read(),y=io.read(),xx=io.read(),yy=io.read(),z=io.read();//scanf("%d %d %d %d %d",&x,&y,&xx,&yy,&z);id[0]=getid(xx,yy),id[1]=getid(x-1,y-1);id[2]=getid(xx,y-1),id[3]=getid(x-1,yy);update(id,ran[z]);update1(id,1);}for(int i=n;i>=1;i--){for(int j=m-1;j>=1;j--) id[0]=getid(i,j),id[1]=getid(i,j+1),v1[id[0]]+=v1[id[1]],v2[id[0]]+=v2[id[1]];}for(int i=m;i>=1;i--){for(int j=n-1;j>=1;j--) id[0]=getid(j,i),id[1]=getid(j+1,i),v1[id[0]]+=v1[id[1]],v2[id[0]]+=v2[id[1]];}int ans=0;For(i,1,n){For(j,1,m){id[0]=getid(i,j);if(v1[id[0]]!=v2[id[0]]*ran[v[id[0]]]){ans++;}}}cout<<ans<<"\n";return 0;
}
二维前缀和+二进制
也是一个不错的想法,感觉做题的意思就在于可以见识到很多自己可能想不到的思路。
首先,我们可以知道,任意两个不同的数,二进制形式下至少有一位不同。根据这一条件,我们可以将种类进行拆位,因为n*m<=1e6,所以可以拆成二十位,维护每一位的0和1个数的和。若某个位置被不同种类的农药撒过,则一定至少存在一位,该位置的种类在这一位上为1(或为0),而该位置的0(或1)的个数和在这一位上大于0。
对于每次撒农药(x1,y1,x2,y2,k),用四十个差分二维数组,维护区域的0和1的个数。
然后,对每个数组做二维前缀和,得到每个位置的每一位的0和1的个数。
对每个位置种类的每一位进行判断,若该位为0且该位1的个数大于0,或者该位为1且该位0的个数大于0,则说明该位置的植物死亡。
时间复杂度O( n*m*log(n*m) ),log(n*m)来自拆位
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (root<<1)
#define rs ((root<<1)|1)
const int M=1e6+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e7;
struct FastIO//输入挂
{static const int S=200;int wpos;char wbuf[S];FastIO():wpos(0){}inline int xchar(){static char buf[S];static int len=0,pos=0;if(pos==len) pos=0,len=fread(buf,1,S,stdin);if(pos==len) exit(0);return buf[pos++];}inline int read(){int s=1,c=xchar(),x=0;while(c<=32) c=xchar();if(c=='-') s=-1,c=xchar();for(;'0'<=c&&c<='9';c=xchar()) x=x*10+c-'0';return x*s;}~FastIO(){if(wpos) fwrite(wbuf,1,wpos,stdout),wpos=0;}
}io;
int t,n,m;
int getid(int x,int y){if(x-1<0||y-1<0) return 0;return (x-1)*m+y;
}
int v[M],v1[2][20][M];void update(int id[],int x,int z){v1[z&1][x][id[0]]+=1;v1[z&1][x][id[1]]+=1;v1[z&1][x][id[2]]-=1;v1[z&1][x][id[3]]-=1;
}
int main(){n=io.read(),m=io.read(),t=io.read();For(i,1,n){For(j,1,m){v[getid(i,j)]=io.read();}}int x,y,xx,yy,z,id[4];For(i,1,t){x=io.read(),y=io.read(),xx=io.read(),yy=io.read(),z=io.read();id[0]=getid(xx,yy),id[1]=getid(x-1,y-1);id[2]=getid(xx,y-1),id[3]=getid(x-1,yy);for(int j=0;j<=19;j++){update(id,j,z);z>>=1;}}for(int i=n;i>=1;i--){for(int j=m-1;j>=1;j--){id[0]=getid(i,j),id[1]=getid(i,j+1);for(int k=0;k<=1;k++){for(z=0;z<=19;z++){v1[k][z][id[0]]+=v1[k][z][id[1]];}}}}for(int i=m;i>=1;i--){for(int j=n-1;j>=1;j--){id[0]=getid(j,i),id[1]=getid(j+1,i);For(k,0,1){for(z=0;z<=19;z++){v1[k][z][id[0]]+=v1[k][z][id[1]];}}}}int ans=0;For(i,1,n){For(j,1,m){id[0]=getid(i,j);for(int i=0;i<=19;i++){if(v1[!(v[id[0]]&(1<<i))][i][id[0]]>0){ans++;break;}}}}cout<<ans<<"\n";return 0;
}
二维树状数组暴力
这种做法是先将所有农药都洒了,对于每次操作(x1,y1,x2,y2,k),通过树状数组区间修改,将对应区域都加1。
然后枚举植物种类,枚举时,先减去所有该种农药的影响——即对于每次该种操作,对应区域减1。此时已经不存在同种农药了,再枚举该种植物的位置,其中不为0的显然受到了别的农药的影响,因此可判定死亡。
枚举完每一种植物位置后,别忘了把影响加回去。
当然,这样处理还需要按植物的种类存储好位置,并按农药的种类存储好操作。
时间复杂度O(n*m*log(n)*log(m)),log(n)*log(m)来自二维树状数组
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (root<<1)
#define rs ((root<<1)|1)
const int M=1e6+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e7;
struct FastIO//输入挂
{static const int S=200;int wpos;char wbuf[S];FastIO():wpos(0){}inline int xchar(){static char buf[S];static int len=0,pos=0;if(pos==len) pos=0,len=fread(buf,1,S,stdin);if(pos==len) exit(0);return buf[pos++];}inline int read(){int s=1,c=xchar(),x=0;while(c<=32) c=xchar();if(c=='-') s=-1,c=xchar();for(;'0'<=c&&c<='9';c=xchar()) x=x*10+c-'0';return x*s;}~FastIO(){if(wpos) fwrite(wbuf,1,wpos,stdout),wpos=0;}
}io;
int t,n,m,c[M];
int getid(int x,int y){return (x-1)*m+y;
}
struct node{int x,y;node(int _x=0,int _y=0):x(_x),y(_y){}
};
struct node1{int x,y,xx,yy;node1(int _x=0,int _y=0,int _xx=0,int _yy=0):x(_x),y(_y),xx(_xx),yy(_yy){}
};
vector<node> v[M];
vector<node1> v1[M];
int lowbit(int x){return x&(-x);
}
void add(int x,int y,int z){if(x<=0||y<=0) return;int tmpy=y;while(x<=n){y=tmpy;while(y<=m){c[getid(x,y)]+=z;y+=lowbit(y);}x+=lowbit(x);}
}
int sum(int x,int y){int res=0,tmpy=y;while(x>0){y=tmpy;while(y>0){res+=c[getid(x,y)];y-=lowbit(y);}x-=lowbit(x);}return res;
}
void update(int x,int y,int xx,int yy,int z){add(x,y,z);add(xx+1,yy+1,z);add(xx+1,y,-z);add(x,yy+1,-z);
}
int main(){
// freopen("in.txt","r",stdin);n=io.read(),m=io.read(),t=io.read();int x,y,xx,yy,z,id[4];For(i,1,n){For(j,1,m){z=io.read();v[z].p_b(node(i,j));}}For(i,1,t){x=io.read(),y=io.read(),xx=io.read(),yy=io.read(),z=io.read();v1[z].p_b(node1(x,y,xx,yy));update(x,y,xx,yy,1);}int ans=0;For(i,1,n*m){if(v[i].size()>0){for(node1 tt:v1[i]){update(tt.x,tt.y,tt.xx,tt.yy,-1);}for(node tt:v[i]){if(sum(tt.x,tt.y)>0) ans++;}for(node1 tt:v1[i]){update(tt.x,tt.y,tt.xx,tt.yy,1);}}}cout<<ans<<"\n";return 0;
}