【dtoj#5093】淘淘种地

博客围绕农田施肥问题展开,淘淘有n×m大小的农田,蓝蓝施肥T次,若肥料与植物类型不符植物会死,需计算死亡植物数量。题解提到暴力法可得70分,还介绍了二维前缀和、拆位思想等方法,以及作者在代码实现中遇到T、RE、MLE等问题后最终AC的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题目描述

淘淘有一块长方形的 n × m n \times m n×m 大小的农田,农田的每一格上种着一种植物,在第 i i i 行,第 j j j 列的植物是第 a [ i ] [ j ] a[i][j] a[i][j] 种。
热心的蓝蓝想帮淘淘施肥,但是第 i i i 种肥料只能对第 i i i 种植物起作用,如果第 i i i 种肥料施在第 j j j 种植物上且 i ≠ j i \neq j i=j,这一株植物就会死。现在蓝蓝施肥了 T T T 次,第 i i i 次施肥会把第 k [ i ] k[i] k[i] 种肥料给矩阵 [ x 1 [ i ] . . . x 2 [ i ] ] [ y 1 [ i ] . . . y 2 [ i ] ] [x1[i]...x2[i]][y1[i]...y2[i]] [x1[i]...x2[i]][y1[i]...y2[i]] 中的所有植物。
淘淘看到自己有植物死了,十分伤心,你要求出有多少植物死了。

输入格式

第一行三个整数 n , m n,m n,m T T T
接下来 n n n 行每行 m m m 个整数,第 i i i 行第 j j j 个整数表示 a [ i ] [ j ] a[i][j] a[i][j]
接下来 T T T 行每行五个整数, x 1 [ i ] , y 1 [ i ] , x 2 [ i ] , y 2 [ i ] , k [ i ] x1[i], y1[i], x2[i], y2[i], k[i] x1[i],y1[i],x2[i],y2[i],k[i],表示一次施肥。

输出格式

一行一个整数表示有多少植物死了。

样例输入
2 2 2
1 2
2 3
1 1 2 2 2
2 1 2 1 1
样例输出
3
数据范围与提示

对于 100 % 100\% 100% 的数据,满足 1 ≤ n × m ≤ 1000000 , 1 ≤ T ≤ 1000000 , 1 ≤ a [ i ] [ j ] , k [ i ] ≤ n × m , 1 ≤ x 1 [ i ] ≤ x 2 [ i ] ≤ n , 1 ≤ y 1 [ i ] ≤ y 2 [ i ] ≤ m 1 \leq n \times m \leq 1000000,1 \leq T \leq 1000000,1 \leq a[i][j], k[i] \leq n \times m,1 \leq x1[i] \leq x2[i] \leq n,1 \leq y1[i] \leq y2[i] \leq m 1n×m1000000,1T1000000,1a[i][j],k[i]n×m,1x1[i]x2[i]n,1y1[i]y2[i]m
S u b t a s k   1 ( 30 p t s ) Subtask\ 1(30pts) Subtask 1(30pts) : 保证 1 ≤ n × m × T ≤ 1 0 7 1 \leq n \times m \times T \leq 10^7 1n×m×T107
S u b t a s k   2 ( 40 p t s ) Subtask\ 2(40pts) Subtask 2(40pts) : 满足 1 ≤ a [ i ] [ j ] , k [ i ] ≤ 2 1 \leq a[i][j], k[i] \leq 2 1a[i][j],k[i]2
S u b t a s k   3 ( 30 p t s ) Subtask\ 3(30pts) Subtask 3(30pts) : 无任何其他限制。

题解

首先oj上数据太水,直接暴力竟然可以70分……
看到对一个矩阵施肥,不难想到二维前缀和。考场上想不到暴力分那么多,对于 S u b t a s k   2 Subtask\ 2 Subtask 2大都会对两种类型分别进行前缀和统计,可以算出每种植物被施肥的次数(其实只要知道有没有被施肥就可以)。
对付多种类型,这里用到的是拆位思想。
我们把肥料看作对每一个二进制位施0或1的肥。要想一种植物不死,这种植物为1的二进制位上不能被施0的肥,为0的二进制位上不能被施1的肥,每一位分开做即可。
这个思路显然是正确的,但我的经历充分证明了思路正确离代码AC还有十万八千里……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map> 
using namespace std;
map < int,map<int,int > > tp,flg;
map < int,map<int,map<int,map<int,int> > > > c,qzh;
int n,m,t,ans,xi,yi,xj,yj,k,mxk;
int main(){
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&tp[i][j]);
		}
	}
	for(int i=1;i<=t;i++){
		scanf("%d%d%d%d%d",&xi,&yi,&xj,&yj,&k);
		mxk=max(mxk,k);
		for(int j=0;(1<<j)<=k;j++){
			if((1<<j)&k){
				c[xi][yi][j][1]++;
				c[xj+1][yj+1][j][1]++;
				c[xi][yj+1][j][1]--;
				c[xj+1][yi][j][1]--;
				continue;
			}
			c[xi][yi][j][0]++;
			c[xj+1][yj+1][j][0]++;
			c[xi][yj+1][j][0]--;
			c[xj+1][yi][j][0]--;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int q=0;(1<<q)<=mxk;q++){ 
				qzh[i][j][q][0]=qzh[i][j-1][q][0]+qzh[i-1][j][q][0]-qzh[i-1][j-1][q][0]+c[i][j][q][0];
				qzh[i][j][q][1]=qzh[i][j-1][q][1]+qzh[i-1][j][q][1]-qzh[i-1][j-1][q][1]+c[i][j][q][1];
				int cc=1<<q;
				if(flg[i][j]==0&&((qzh[i][j][q][1]>0&&(tp[i][j]&cc)==0)||(qzh[i][j][q][0]>0&&(tp[i][j]&cc)>0))){ 
					flg[i][j]=1;
					ans++;
				} 
			} 
		}
	}
	printf("%lld\n",ans);
	return 0;
}

这是我一开始的代码,蒟蒻的我对于不定长的数据范围居然想用map,顺理成章地T……后来改用vector,当然,大同小异
我百度了一会发现下面这种方法或许能用了,于是:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,t,ans,xi,yi,xj,yj,k,mxk;
int main(){
	scanf("%d%d%d",&n,&m,&t);
	int tp[n+10][m+10],flg[n+10][m+10],c[n+10][m+10][21][2],qzh[n+10][m+10][21][2];
	memset(flg,0,sizeof(flg));
	memset(c,0,sizeof(c));
	memset(qzh,0,sizeof(qzh));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&tp[i][j]);
		}
	}
	for(int i=1;i<=t;i++){
		scanf("%d%d%d%d%d",&xi,&yi,&xj,&yj,&k);
		mxk=max(mxk,k);
		for(int j=0;j<=20;j++){
			if(((1<<j)&k)>0){
				c[xi][yi][j][1]++;
				c[xj+1][yj+1][j][1]++;
				c[xi][yj+1][j][1]--;
				c[xj+1][yi][j][1]--;
				continue;
			}
			c[xi][yi][j][0]++;
			c[xj+1][yj+1][j][0]++;
			c[xi][yj+1][j][0]--;
			c[xj+1][yi][j][0]--;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int q=0;q<=20;q++){ 
				qzh[i][j][q][0]=qzh[i][j-1][q][0]+qzh[i-1][j][q][0]-qzh[i-1][j-1][q][0]+c[i][j][q][0];
				qzh[i][j][q][1]=qzh[i][j-1][q][1]+qzh[i-1][j][q][1]-qzh[i-1][j-1][q][1]+c[i][j][q][1];
				int cc=1<<q;
				if(flg[i][j]==0&&((qzh[i][j][q][1]>0&&(tp[i][j]&cc)==0)||(qzh[i][j][q][0]>0&&(tp[i][j]&cc)>0))){ 
					flg[i][j]=1;
					ans++;
				}
			} 
		}
	}
	printf("%d\n",ans);
	return 0;
}

RE
于是我把二维数据折叠到一维数组:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define pos(x, y) ((x) * (m + 2) + y)
using namespace std;
int n,m,t,ans,xi,yi,xj,yj,k,mxk;
int tp[4000000],flg[4000000],c[4000000][21][2],qzh[4000000][21][2];
int main(){
	//freopen("farm.in","r",stdin);
	//freopen("farm.out","w",stdout);
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&tp[pos(i,j)]);
		}
	}
	for(int i=1;i<=t;i++){
		scanf("%d%d%d%d%d",&xi,&yi,&xj,&yj,&k);
		mxk=max(mxk,k);
		for(int j=0;j<=20;j++){
			if(((1<<j)&k)>0){
				c[pos(xi,yi)][j][1]++;
				c[pos(xj+1,yj+1)][j][1]++;
				c[pos(xi,yj+1)][j][1]--;
				c[pos(xj+1,yi)][j][1]--;
				continue;
			}
			c[pos(xi,yi)][j][0]++;
			c[pos(xj+1,yj+1)][j][0]++;
			c[pos(xi,yj+1)][j][0]--;
			c[pos(xj+1,yi)][j][0]--;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int q=0;q<=20;q++){ 
				qzh[pos(i,j)][q][0]=qzh[pos(i,j-1)][q][0]+qzh[pos(i-1,j)][q][0]-qzh[pos(i-1,j-1)][q][0]+c[pos(i,j)][q][0];
				qzh[pos(i,j)][q][1]=qzh[pos(i,j-1)][q][1]+qzh[pos(i-1,j)][q][1]-qzh[pos(i-1,j-1)][q][1]+c[pos(i,j)][q][1];
				int cc=1<<q;
				if(flg[pos(i,j)]==0&&((qzh[pos(i,j)][q][1]>0&&(tp[pos(i,j)]&cc)==0)||(qzh[pos(i,j)][q][0]>0&&(tp[pos(i,j)]&cc)>0))){ 
					flg[pos(i,j)]=1;
					ans++;
				}
			} 
		}
	}
	printf("%d\n",ans);
	return 0;
}

MLE
最后把遍历每一位的循环放到最外面,可以优化掉一维:(AC代码)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define pos(x, y) ((x) * (m + 2) + y)
using namespace std;
int n,m,t,ans,xi,yi,xj,yj,k,mxk;
int tp[4000000],cw[4000000],c[4000000][2];
bool flag[4000000];
struct query{
	int xi,yi,xj,yj,k;
}Q[1000005];
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;
}
int main(){
	n=read();m=read();t=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			tp[pos(i,j)]=read();
	for(int i=1;i<=t;i++){
		Q[i].xi=read();
		Q[i].yi=read();
		Q[i].xj=read();
		Q[i].yj=read();
		Q[i].k=read();
	}
	for(int kk=0;kk<20;kk++){
		for(int i=0;i<=n+1;i++)
            for(int j=0;j<=m+1;j++) 
				c[pos(i,j)][0]=c[pos(i,j)][1]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) 
				cw[pos(i,j)]=(tp[pos(i,j)]>>kk) & 1;
		for(int i=1;i<=t;i++){
			int tt=Q[i].k & 1;
			Q[i].k>>=1;
			c[pos(Q[i].xi,Q[i].yi)][tt]++;
			c[pos(Q[i].xj+1,Q[i].yj+1)][tt]++;
			c[pos(Q[i].xi,Q[i].yj+1)][tt]--;
			c[pos(Q[i].xj+1,Q[i].yi)][tt]--;
		}
		for(int i=1;i<=n;i++) 
			for(int j=1;j<=m;j++){
				c[pos(i,j)][0]+=c[pos(i,j-1)][0]+c[pos(i-1,j)][0]-c[pos(i-1,j-1)][0];
				c[pos(i,j)][1]+=c[pos(i,j-1)][1]+c[pos(i-1,j)][1]-c[pos(i-1,j-1)][1];
			}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if(c[pos(i,j)][cw[pos(i,j)]^1])
					flag[pos(i,j)]=true;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(flag[pos(i,j)]) ans++; 
	printf("%d\n",ans);
	return 0;
}

Thanks!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值