其他分享
首页 > 其他分享> > Luogu P3801 红色的幻想乡

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