重新填坑——牛客暑期多校第二场 J farm( 二维前缀和+随机/二进制 /二维树状数组暴力/...)

这道题的做法很多。

二维前缀和+随机

用两个差分二维数组,一个存种类和,另一个存总次数,对于每一次撒农药操作(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;
}

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/89180080