• CF799F Beautiful fountains rows


    CF799F Beautiful fountains rows

    前言

    感觉这3500还挺简单的?可能是因为年代久远的原因?

    F Beautiful fountains rows

    链接

    https://codeforces.ml/contest/799/problem/F

    题意

    n行m列的01矩阵,对于第i行(l_i<=x<=r_i)的是1,其余都是0。数对(a,b)是好的,当且仅当第a列到第b列的矩阵有值为1的点且对于每一行在a到b之间的1的个数为0或奇数。求所有好数对的b-a+1的总和。(1leq n,m leq 200000,1leq l_i leq r_i leq m)

    题解

    显然答案跟每一行的顺序无关。。
    对于每一行,会导致一些数对(a.b)不好
    根据数据结构的套路,这种时候肯定要枚举一个端点。。
    于是从左向右枚举左端点a。
    我们需要维护左端点固定时,右端点有哪些选择。
    首先肯定要维护一下当前还剩哪些1区间需要考虑。
    现在存在的1区间分两种:1.整段都在大于a的位置 2.从a开始就有1
    然后考虑有哪些情况会导致一个数对不好。
    1.全0,这个我们需要维护一下最近从几开始有1.
    2.右端点的选择与第二类区间产生矛盾(偶数个1)
    3.右端点的选择与第一类区间产生矛盾(偶数个1)
    对于2,我们可以维护一下所有这类区间的终点,根据终点的奇偶分类讨论。然后对于3,根据起点,终点,长度的奇偶性各种讨论。。。
    这样肯定是不好写的QAQ。。于是我们考虑再次离线的策略。
    先讨论比较容易讨论的2 。并且将右端点分奇偶两类考虑。
    与a同奇偶的右端点,只要不超过终点与a奇偶不同的区间就行。
    与a不同奇偶的右端点,要不存在终点与a奇偶不同的区间,并且超过终点与a奇偶相同的区间。
    显然这两个合法的部分都是连续的一段奇数或偶数。
    然后把这些连续的段作为3的询问,看这些段里有多少能作为右端点。
    3的区间会导致不好的右端点也需要讨论。
    如果区间长度是偶数,说明区间后面的都不行。
    然后区间内与区间左端L奇偶不同的也一定不行。
    于是问题就变成了区间清零,区间询问的问题,搞个线段树就行了,要分奇偶询问。

    (Code)

    #include<bits/stdc++.h>
    #define LL long long
    #define LD long double
    using namespace std;
    const LL P=998244353;
    const int N=3e5+10;
    const int INF=1e9;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void print(LL x){
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    void pls(LL &x,LL y){
        x+=y;if(x>=P)x-=P;
    }
    int n,m;
    struct Seg{
        int l,r; 
    }a[N];
    bool cmp(Seg x,Seg y){
        return x.l<y.l;
    }
    multiset<int> ed[2];
    multiset<int>::iterator it;
    int cnt;
    struct Query{
        int st,op,l,r;
    }q[N<<1];
    struct Node{
        int l,r;
        LL num[2];
        LL sum[2];
        bool laz[2];
    }d[N<<2];
    #define ls id<<1
    #define rs id<<1|1
    void pushup(int id){
        if(d[id].l==d[id].r) return;
        d[id].num[0]=d[ls].num[0]+d[rs].num[0];
        d[id].num[1]=d[ls].num[1]+d[rs].num[1];
        d[id].sum[0]=d[ls].sum[0]+d[rs].sum[0];
        d[id].sum[1]=d[ls].sum[1]+d[rs].sum[1];
        return;
    }
    void pushdown(int id){
        if(d[id].l==d[id].r) return;
        if(!d[id].laz[0]){
            d[id].laz[0]=1;
            d[ls].num[0]=d[rs].num[0]=0;
            d[ls].sum[0]=d[rs].sum[0]=0;
            d[ls].laz[0]=d[rs].laz[0]=0;
        }
        if(!d[id].laz[1]){
            d[id].laz[1]=1;
            d[ls].num[1]=d[rs].num[1]=0;
            d[ls].sum[1]=d[rs].sum[1]=0;
            d[ls].laz[1]=d[rs].laz[1]=0;
        }
        return;
    }
    void build(int l,int r,int id){
        d[id].l=l;d[id].r=r;
        d[id].num[0]=d[id].num[1]=d[id].sum[0]=d[id].sum[1]=0;
        d[id].laz[0]=d[id].laz[1]=1;
        if(l==r){
            d[id].num[l&1]++;
            d[id].sum[l&1]+=l;
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,id<<1);
        build(mid+1,r,id<<1|1);
        pushup(id);
        return;
    }
    
    void update(int l,int r,int id,int op){
        pushdown(id);
        if(d[id].l==l&&d[id].r==r){
            d[id].num[op]=d[id].sum[op]=0;
            d[id].laz[op]=0;
            return;
        }
        if(r<=d[ls].r) update(l,r,ls,op);
        else if(l>d[ls].r) update(l,r,rs,op);
        else{
            update(l,d[ls].r,ls,op);
            update(d[rs].l,r,rs,op);
        }
        pushup(id);
        return;
    }
    
    LL res[2][2];
    
    void ask(int l,int r,int id){
        pushdown(id);
        if(d[id].l==l&&d[id].r==r){
            res[0][0]+=d[id].num[0];
            res[1][0]+=d[id].num[1];
            res[0][1]+=d[id].sum[0];
            res[1][1]+=d[id].sum[1];
            return;
        }
        if(r<=d[ls].r) ask(l,r,ls);
        else if(l>d[ls].r) ask(l,r,rs);
        else{
            ask(l,d[ls].r,ls);
            ask(d[rs].l,r,rs);
        }
        return;
    }
    
    void MAIN(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            scanf("%d%d",&a[i].l,&a[i].r);
        }
        sort(a+1,a+1+n,cmp);
        cnt=0;
        int cur,L,R;
        for(int i=1,j=1;i<=m;++i){
            while(j<=n&&a[j].l==i){
                ed[a[j].r&1].insert(a[j].r);
                ++j;
            }
            while(!ed[0].empty()){
                it=ed[0].begin();
                if((*it)<i) ed[0].erase(it);
                else break;
            }
            while(!ed[1].empty()){
                it=ed[1].begin();
                if((*it)<i) ed[1].erase(it);
                else break;
            }
            R=m;L=R+1;
            if((!ed[1].empty())||(!ed[0].empty())) L=i;
            if(j<=n) L=min(L,a[j].l);
            cur=i&1;
            if(ed[cur^1].empty()){
                q[++cnt]=(Query){i,cur,L,R};
            }
            else{
                it=ed[cur^1].begin();
                q[++cnt]=(Query){i,cur,L,(*it)};
            }
            if(!ed[cur^1].empty()){
                continue;
            }
            if(ed[cur].empty()){
                q[++cnt]=(Query){i,cur^1,L,R};
            }
            else{
                it=ed[cur].end();
                --it;
                q[++cnt]=(Query){i,cur^1,max(L,(*it)+1),R};
            }
        }
        build(1,m,1);
        LL ans=0;
        for(int i=cnt,j=n;i>=1;--i){
            while(j>=1&&a[j].l>q[i].st){
                cur=a[j].l&1;
                if((a[j].r-a[j].l)&1){
                    if(a[j].r+1<=m) update(a[j].r+1,m,1,cur);
                    update(a[j].l,m,1,cur^1);
                }
                else{
                    update(a[j].l,a[j].r,1,cur^1);
                }
                --j;
            }
            res[0][0]=res[0][1]=res[1][0]=res[1][1]=0;
            if(q[i].l<=q[i].r){
                ask(q[i].l,q[i].r,1);
                ans+=res[q[i].op][1];
                ans-=(LL)(q[i].st-1)*res[q[i].op][0];
            }
        }
        printf("%lld
    ",ans);
        return;
    }
    int main(){
        int ttt=1;
        while(ttt--) MAIN();
        return 0;    
    }
    
  • 相关阅读:
    自然数幂和的若干种解法
    线性预处理逆元
    差分与有限微积分
    UVALive 6859——凸包&&周长
    UVALive 6858——分类讨论&&水题
    UVALive 6862——结论题&&水题
    ZOJ4019——贪心&&DP
    [LeetCode] Power of Two
    循环队列实现(C++) Ring Buffer
    正确使用stl vecotr erase函数
  • 原文地址:https://www.cnblogs.com/Yuigahama/p/14587671.html
Copyright © 2020-2023  润新知