Codeforces 707E 二维树状数组

博客讲述了如何使用二维树状数组来维护一个n行m列棋盘上k个连续灯串的状态,每个灯串可以整体开关,并能快速查询以指定矩形区域的灯的值之和。通过last和now数组记录灯串状态,仅在查询时更新树状数组,降低了时间复杂度。代码实现包括了树状数组的数据结构和查询、修改操作。
摘要由CSDN通过智能技术生成

给定n行m列的棋盘,棋盘上有k个连续的灯串,每个灯串的每个灯都有一个值,开启的时候获取这个值,关闭的时候是0,每个灯串的所有灯要么一起关,要么一起开。

m个询问,switch k,调整序号为k的灯串的开关状态,ask x,y,n,m,询问以(x,y)和(n,m)为对角顶点的矩形的值之和。

可以用二维的树状数组来维护这个棋盘,每个树状数组维护一行,计算的时候我们只需暴力求和。

对于switch操作,我们是不必每次都更新到我们的树状数组上的,因为switch完后未必查询,并且状态是摇摆的,也就是说可能有switch switch ask的情况,两次swicth等于没有,所以我们只需要在查询的时候更新树状数组,因此我们需要last数组维护上一次查询的时候的灯串状态,now数组维护现在的状态,查询的时候只要他们不同,就更新即可。

理论上最坏时间复杂度应该是修改一次查询一次交替进行,并且每次查询都包括所有行(不一定所有列,因为树状数组维护的是行,时间消耗主要在行数上),每次修改的灯串是装满棋盘的一个灯串。最坏时间复杂度应该是m/2*n*m*logn+m/2*n,也就是修改次数*元素个数(行数*列数)*单点修改时间logn+行树状数组查询时间(每行是O(1),那么总和就是m/2*n),算下来应该是1e10阶级的,但是cf能过,应该是数据不够强大。

#include<bits/stdc++.h>
#define ll long long
#define fr first
#define se second
#define x1 x_
#define x2 x__
#define y1 y_
#define y2 y__
using namespace std;

int lowbit(int x){return -x&x;}

struct tree_array
{
	ll tree[2005];
	ll getsum(int i)
	{
		ll ret=0;
		while(i)
		{
			ret+=tree[i];
			i-=lowbit(i);
		}
		return ret;
	}
	ll query_sum(int l,int r){return getsum(r)-getsum(l-1);}
	void add(int i,int k)
	{
		while(i<=2000)
		{
			tree[i]+=k;
			i+=lowbit(i);
		}
	}
}a[2005];

int n,m,k,cnt[2005],v[2005][2005];
bool now[2005],last[2005];
pair<int,int>pos[2005][2005];//pos[i][j]->第i条的第j个元素

void solve(int k)
{
	int base;
	if(last[k]) base=-1;
	else base=1;
	for(int i=1;i<=cnt[k];i++)
	{
		a[pos[k][i].fr].add(pos[k][i].se,base*v[pos[k][i].fr][pos[k][i].se]);
	}
}

ll query(int x1,int y1,int x2,int y2)
{
	ll ret=0;
	for(int i=x1;i<=x2;i++) ret+=a[i].query_sum(y1,y2);
	return ret;
}

int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
	{
		last[i]=now[i]=true;
		int t;cin>>t;
		while(t--)
		{
			int x,y;cin>>x>>y;
			pos[i][++cnt[i]]=make_pair(x,y);
			cin>>v[x][y];
			a[x].add(y,v[x][y]);
		}
	}
	int q;cin>>q;
	vector<int>v1(5);
	while(q--)
	{
		string s;cin>>s;
		if(s=="SWITCH")
		{
			int x;cin>>x;
			now[x]=!now[x];
		}
		else
		{
			for(int i=1;i<=k;i++)
			{
				if(now[i]!=last[i])
				{
					solve(i);
					last[i]=now[i];
				}
			}
			for(int i=1;i<=4;i++) cin>>v1[i];
			printf("%lld\n",query(v1[1],v1[2],v1[3],v1[4]));
		}
	}
 	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值