Luogu P3801 红色的幻想乡
作者:互联网
据说NOIP前写题解可以\(\mathtt{RP}\)++
20pts
我会暴力!
40pts
我会二维树状数组(线段树)!
AC
\(n,m,q<=100000\),二维肯定会炸。考虑每次修改都是修改一整行和一整列,可以分别对行和列建线段树,统计区间内有多少行、列被放过红雾。
因为我们用线段树统计了有多少行、列被修改过,但询问的是有多少点有红雾,所以答案应为\(x\times len_n+y\times len_m\),其中x,y表示区间内有红雾的行、列数,\(len\)表示对应的区间长度。
但这么做肯定是不对的,因为行列的交点被我们算了两次,而它们应该没有红雾。所以我们应用容斥原理,将它们减去就好了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
#define LL long long
using namespace std;
struct zzz{ //线段树统计有红雾的行、列
int tree[100010<<2];
inline void up(int p){
tree[p]=tree[ls]+tree[rs];
}
void update(int l,int r,int p,int pos){
if(l==r){
tree[p]^=1; return ; //因为在已经有红雾的行和列上放红雾会全部抵消,所以是xor操作
}
if(pos<=mid) update(l,mid,ls,pos);
else update(mid+1,r,rs,pos);
up(p);
}
int query(int l,int r,int p,int nl,int nr){
int ans=0;
if(nl<=l&&r<=nr) return tree[p];
if(nl<=mid) ans+=query(l,mid,ls,nl,nr);
if(nr>mid) ans+=query(mid+1,r,rs,nl,nr);
return ans;
}
}hang,lie;
int read(){
int k=0; char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
k=k*10+c-48, c=getchar();
return k;
}
int main(){
int n=read(),m=read(),q=read();
for(int i=1;i<=q;i++){
int opt=read();
if(opt==1){
int x=read(),y=read();
hang.update(1,n,1,x);
lie.update(1,n,1,y);
}
else{
LL a=read(),b=read(),c=read(),d=read();
LL hh=hang.query(1,n,1,a,c);
LL ll=lie.query(1,n,1,b,d);
LL ans=hh*(d-b+1)+ll*(c-a+1)-2*hh*ll; //如上文式子所说,有红雾的行列数*行列长度-交点*2
printf("%lld\n",ans);
}
}
return 0;
}
标签:幻想,int,Luogu,线段,len,ans,红雾,include,P3801 来源: https://www.cnblogs.com/morslin/p/11854976.html