【洛谷 3801】红色的幻想乡

【题目】

传送门

题目背景:

蕾米莉亚的红雾异变失败后,很不甘心。

题目描述:

经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放。

我们将幻想乡看做是一个 n × m n\times m n×m 的方格地区,一开始没有任何一个地区被红雾遮盖。蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行和整列,但不会影响到她所站的那个地区。如果两阵红雾碰撞,则会因为密度过大而沉降消失。灵梦察觉到了这次异变,决定去解决它。但在解决之前,灵梦想要了解一片范围红雾的密度。可以简述为两种操作:

1 1 1 x x x y y y:蕾米莉亚站在坐标 ( x , y ) (x,y) (x,y) 的位置向四个方向释放无限长的红雾。

2 2 2 x 1 x_1 x1 y 1 y_1 y1 x 2 x_2 x2 y 2 y_2 y2 询问左上点为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右下点为 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 的矩形范围内,被红雾遮盖的地区的数量。

输入格式:

第一行三个整数 n , m , q n,m,q n,m,q,表示幻想乡大小为 n × m n\times m n×m,有 q q q 个询问。

接下来 q q q 行,每行 3 3 3 个或 5 5 5 个整数,用空格隔开,含义见题目描述。

输出格式:

对于每一个操作 2 2 2,输出一行一个整数,表示对应询问的答案。

样例数据:

输入
4 4 3
1 2 2
1 4 4
2 1 1 4 4

输出
8

样例解释:

o 表示没有红雾,x 表示有红雾,两次释放红雾后幻想乡地图如下:

oxox
xoxo
oxox
xoxo

数据范围:

对于 20 % 20\% 20% 的数据, 1 ≤ n , m , q ≤ 200 1\le n,m,q\le200 1n,m,q200

对于 40 % 40\% 40% 的数据, 1 ≤ n , m , q ≤ 1000 1\le n,m,q\le1000 1n,m,q1000

对于 100 % 100\% 100% 的数据, 1 ≤ n , m , q ≤ 100000 1\le n,m,q\le100000 1n,m,q100000

1 ≤ x 1 , x 2 , x ≤ n 1\le x_1,x_2,x\le n 1x1,x2,xn x 1 ≤ x 2 x_1\le x_2 x1x2

1 ≤ y 1 , y 2 , y ≤ m 1\le y_1,y_2,y\le m 1y1,y2,ym y 1 ≤ y 2 y_1\le y_2 y1y2


【分析】

一开始看到这道题,想到用二维树状数组(二维线段树)来做,但看到数据范围。。。

然后脑补了一些鬼畜的想法,最后发现自己想复杂了

我们对于行和列都分别建一颗线段树,分别维护这一行(或这一列)有没有红雾

1 1 1 表示有红雾,用 0 0 0 表示没有红雾的话

对于每个点,如果只有行是 1 1 1 或者只有列是 1 1 1,那么这个点就有红雾(可以画个图理解一下)

那么修改就好办了,直接对行和列异或 1 1 1 就行了

那么询问怎么搞呢?

把 [ x 1 x_1 x1 , x 2 x_2 x2 ] 中 1 1 1 的数量记为 n u m x , 1 num_{x,1} numx,1 0 0 0 的数量记为 n u m x , 0 num_{x,0} numx,0,对于 y y y 同理

显然的,由于要找 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 的有红雾的点的个数,那就是 n u m x , 1 × n u m y , 0 + n u m x , 0 × n u m y , 1 num_{x,1}\times num_{y,0}+num_{x,0}\times num_{y,1} numx,1×numy,0+numx,0×numy,1

那么用线段树乱搞一下就可以了


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int sum[2][N<<2];
void modify(int root,int l,int r,int x,int k)
{
	if(l==r)
	{
		sum[k][root]^=1;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)  modify(root<<1,l,mid,x,k);
	else  modify(root<<1|1,mid+1,r,x,k);
	sum[k][root]=sum[k][root<<1]+sum[k][root<<1|1];
}
int query(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	  return sum[k][root];
	int ans=0,mid=(l+r)>>1;
	if(x<=mid)  ans+=query(root<<1,l,mid,x,y,k);
	if(y>mid)  ans+=query(root<<1|1,mid+1,r,x,y,k);
	return ans;
}
int main()
{
	int n,m,q,i,s,x,y,x1,y1;
	scanf("%d%d%d",&n,&m,&q);
	for(i=1;i<=q;++i)
	{
		scanf("%d%d%d",&s,&x,&y);
		if(s==1)  modify(1,1,n,x,0),modify(1,1,m,y,1);
		else
		{
			scanf("%d%d",&x1,&y1);
			long long temp1=query(1,1,n,x,x1,0);
			long long temp2=query(1,1,m,y,y1,1);
			long long ans=temp1*(y1-y+1-temp2)+temp2*(x1-x+1-temp1);
			printf("%lld\n",ans);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值