Codeforces Round #439 (Div. 2) E. The Untended Antiquity 二维线段树||二维树状数组

http://codeforces.com/contest/869/problem/E

题意:n*m的矩阵,q次操作,三种类型

类型1:给指定矩阵加上围栏

类型2:给指定矩阵去掉围栏

类型3:查询两点是否存在一条不通过围栏的路

加围栏是全包,也就相当于加了围栏后只能是里面走向里面,外面走向外面,这样就可以给这块平面附一个值,判断能不能走直接判相等就可以了,因为有矩阵叠加的情况,也就是围栏里面加围栏,所以就要平面加值,不能平面替换值。为了防止叠加之后值相同,所以要加一个hash操作。

顺便总结下刚学的二维线段树

1:四叉树写法,感觉这种写法很坑,一旦有区间操作复杂度就不合理,虽然写起来简单,还可以加lazy标记,但感觉没什么用,只有查询最值可能好使一些,

简单说下自己想到的这种写法遇到区间操作复杂度不合理的情况,四叉树每次都要分成四个部分,可以想成四个方块,假如某个查询是查询一块长L很大,宽W很小的区域,为了符合L,必须要分的很小,递归深度很深,也就是说查询块被分成了很多个叠在一起的方块,而较优的查询(树套树写法)是对每一个维度都是类似的线段树分割,也就是说查询块被分成了几个叠在一起的不等长的长块。

2:线段树套线段树写法,只能实现区间更新单点查询或单点更新区间查询,因为第一维线段树无法使用lazy标记(目前没搜到如何使用。。。),局限了只能区间查询,区间更新单点查询里的区间更新也是伪区间更新,用了一个标记永久化的操作,更新的部分不往下放,查询一个点需要考虑他的所有祖先,因此局限了只能单点查询。


二维树状数组

单点更新区间查询是最基本的,也可以实现一些区间更新单点查询,例如本题,我要给某块平面(x1,y1,x2,y2)加k

就像伪线段区间更新一样,左端点加加,右端点减减,每个点对应值为前缀和值,平面依旧可以这样做

(x1,y1)+=k;

(x1,y2+1)-=k;

(x2+1,y1)-=k;

(x2+1,y2+1)+=k;

这样某个点(x,y)对应值应该是矩阵(1,1,x,y)的所有值的和,二维树状数组很容易求


二维线段树(389ms):

#include<bits/stdc++.h>
using namespace std;
const int maxn=2505;
const unsigned long long mo=998244353;
const unsigned long long zero=0;
unsigned long long seg[maxn*2][maxn*2];
int L,R,T,B,n,m;
int X,Y;
unsigned long long k;

inline int id(int le,int ri)
{
	return le+ri|le!=ri;
}

void updatey(int le,int ri,int nodex)
{
	int nodey=id(le,ri);
	if(le>=T&&ri<=B)
	{
		seg[nodex][nodey]+=k;
		return ;
	}
	int mid=(le+ri)>>1;
	if(T<=mid)
	updatey(le,mid,nodex);
	if(B>mid)
	updatey(mid+1,ri,nodex);
}

void updatex(int le,int ri)
{
	int node=id(le,ri);
	if(le>=L&&ri<=R)
	{
		updatey(1,m,node);
		return ;
	}
	int mid=(le+ri)>>1;
	if(L<=mid)
	updatex(le,mid);
	if(R>mid)
	updatex(mid+1,ri);
}

unsigned long long queryy(int nodex,int le,int ri)
{
	int nodey=id(le,ri);
	if(le==ri)
	{
		return seg[nodex][nodey];
	}
	unsigned long long ans=seg[nodex][nodey];
	int mid=(le+ri)>>1;
	if(Y<=mid)
	return ans+queryy(nodex,le,mid);
	else
	return ans+queryy(nodex,mid+1,ri);
}

unsigned long long queryx(int le,int ri)
{
	int node=id(le,ri);
	if(le==ri)
	{
		return queryy(node,1,m);
	}
	unsigned long long ans=queryy(node,1,m);
	int mid=(le+ri)>>1;
	if(X<=mid)
	return ans+queryx(le,mid);
	else
	return ans+queryx(mid+1,ri);
}


unsigned long long hashh(unsigned long long n,unsigned long long m,int le,int ri,int to,int bo)
{
	unsigned long long temp1=(le*n+ri)*m*m;

	unsigned long long temp2=to*m+bo;

	return temp1+temp2;
}
int main()
{
	int q,i,c;
	unsigned long long temp;
	cin>>n>>m>>q;
	while(q--)
	{
		scanf("%d %d %d %d %d",&c,&L,&T,&R,&B);
		if(c==3)
		{
			X=L,Y=T;
			unsigned long long flog1=queryx(1,n);
			X=R,Y=B;
			unsigned long long flog2=queryx(1,n);
		//	cout<<flog1<<" "<<flog2<<endl;
			if(flog1!=flog2)
			{
				printf("No\n");
			}
			else
			{
				printf("Yes\n");
			}
		}
		else
		{
			if(L>R)
			swap(L,R);
			if(T>B)
			swap(T,B);
			if(c==1)
				k=hashh(n,m,L,T,R,B);
			else
				k=zero-hashh(n,m,L,T,R,B);
			updatex(1,n);
		}
	}
	return 0;

 }


二维树状数组(140ms):

#include<bits/stdc++.h>
using namespace std;
const int maxn=2505;
const unsigned long long zero=0;
int L,R,T,B,n,m;
unsigned long long f[maxn][maxn],k;
void add(int x,int y,unsigned long long k)
{
    while(x<=n)
    {
        int ty=y;
        while(ty<=m)
        {
            f[x][ty]+=k;
            ty+=ty&-ty;
        }
        x+=x&-x;
    }
    return ;
}
unsigned long long sum(int x,int y)
{
    unsigned long long ans=0;
    while(x)
    {
        int ty=y;
        while(ty)
        {
            ans+=f[x][ty];
            ty-=ty&-ty;
        }
        x-=x&-x;
    }
    return ans;
}
void update()
{
    add(L,T,k);
    add(R+1,T,zero-k);
    add(L,B+1,zero-k);
    add(R+1,B+1,k);
}
unsigned long long hashh(unsigned long long n,unsigned long long m,int le,int ri,int to,int bo)
{
	unsigned long long temp1=(le*n+ri)*m*m;

	unsigned long long temp2=to*m+bo;

	return temp1+temp2;
}
int main()
{
	int q,i,c;
	unsigned long long temp;
	cin>>n>>m>>q;
	while(q--)
	{
		scanf("%d %d %d %d %d",&c,&L,&T,&R,&B);
		if(c==3)
		{
			unsigned long long flog1=sum(L,T);
			unsigned long long flog2=sum(R,B);
		//	cout<<flog1<<" "<<flog2<<endl;
			if(flog1!=flog2)
			{
				printf("No\n");
			}
			else
			{
				printf("Yes\n");
			}
		}
		else
		{
			if(L>R)
			swap(L,R);
			if(T>B)
			swap(T,B);
			if(c==1)
				k=hashh(n,m,L,T,R,B);
			else
				k=zero-hashh(n,m,L,T,R,B);
			update();
		}
	}
	return 0;

 }

不得不说树状数组既简单,速度还优。。。。


顺便贴一个四叉树写法TLE代码,可能写的不太优雅,自带常数,但自己跑数据试了试,复杂度差的绝不是常数(仅针对下面代码)。。。。。

也可能是自己对四叉树写法理解的还不太深入。。。。。。。。

#include<bits/stdc++.h>
#define ltson le,wm,to,hm
#define lbson le,wm,hm+1,bo
#define rtson wm+1,ri,to,hm
#define rbson wm+1,ri,hm+1,bo
#define son(x) node*4-2+x 
using namespace std;
const int maxn=2505;
const unsigned long long mo=998244353;
const unsigned long long zero=0;
unsigned long long seg[maxn*maxn*4];
unsigned long long lazy[maxn*maxn*4];
int L,R,T,B;
int X,Y;
unsigned long long area(int le,int ri,int to,int bo)
{
	return (ri-le+1)*(bo-to+1); 
}
void pushdown(int node,int le,int ri,int to,int bo)
{
	if(lazy[node])
	{
		int wm=(le+ri)>>1,hm=(to+bo)>>1;
		unsigned long long t;
		t=area(le,wm,to,hm);
		seg[son(0)]+=t*lazy[node];
		
		t=area(le,wm,hm+1,bo);
		seg[son(1)]+=t*lazy[node];
		
		t=area(wm+1,ri,to,hm);
		seg[son(2)]+=t*lazy[node];
		
		t=area(wm+1,ri,hm+1,bo);
		seg[son(3)]+=t*lazy[node];
		
		lazy[son(0)]+=lazy[node];
		lazy[son(1)]+=lazy[node];
		lazy[son(2)]+=lazy[node];
		lazy[son(3)]+=lazy[node];
		lazy[node]=0;
	}
	return ;
}
void pushup(int node)
{
	seg[node]=seg[son(0)]+seg[son(1)]+seg[son(2)]+seg[son(3)];
	return ;
}
void update(unsigned long long k,int le,int ri,int to,int bo,int node)
{
	if(le>ri||to>bo)
	return ;
//	cout<<le<<" "<<ri<<" "<<to<<" "<<bo<<endl; 
	if(le>=L&&ri<=R&&to>=T&&bo<=B)
	{
		unsigned long long t=area(le,ri,bo,to);
		lazy[node]+=k;
		seg[node]+=t*k;
		return ; 
	}
	pushdown(node,le,ri,to,bo);
	int wm=(le+ri)>>1,hm=(to+bo)>>1;
	if(L<=wm)
	{
		if(T<=hm)
		update(k,ltson,son(0));
		if(B>hm)
		update(k,lbson,son(1));	
	}
	if(R>wm)
	{
		if(T<=hm)
		update(k,rtson,son(2));
		if(B>hm)
		update(k,rbson,son(3));
	}
	pushup(node);
}
unsigned long long query(int le,int ri,int to,int bo,int node)
{
	if(le==ri&&to==bo)
	{
		return seg[node];
	}
	pushdown(node,le,ri,to,bo);
	int wm=(le+ri)>>1,hm=(to+bo)>>1;
	if(wm>=X)
	{
		if(hm>=Y)
		return query(ltson,son(0));
		else
		return query(lbson,son(1));	
	}
	else
	{
		if(hm>=Y)
		return query(rtson,son(2));
		else
		return query(rbson,son(3));
	} 
}
unsigned long long hashh(unsigned long long n,unsigned long long m,int le,int ri,int to,int bo)
{
	unsigned long long temp1=(le*n+ri)*m*m;
	
	unsigned long long temp2=to*m+bo;
	
	return temp1+temp2;
}
int main()
{
	int n,m,q,i,c;
	unsigned long long temp;
	cin>>n>>m>>q;
	while(q--)
	{
		scanf("%d %d %d %d %d",&c,&L,&T,&R,&B);
		if(c==3)
		{
			X=L,Y=T;
			unsigned long long flog1=query(1,n,1,m,1);
			X=R,Y=B;
			unsigned long long flog2=query(1,n,1,m,1);
		//	cout<<flog1<<" "<<flog2<<endl;
			if(flog1!=flog2)
			{
				printf("No\n");
			}
			else
			{
				printf("Yes\n");
			}
		}
		else
		{
			if(L>R)
			swap(L,R);
			if(T>B)
			swap(T,B);
			if(c==1)
			{
				update(hashh(n,m,L,T,R,B),1,n,1,m,1);
			}
			else
			{
				update(zero-hashh(n,m,L,T,R,B),1,n,1,m,1);
			}
		}
		
	}
	return 0;
	
 } 




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值