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;
}