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的总和。\(1\leq n,m \leq 200000,1\leq 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\n",ans);
    return;
}
int main(){
    int ttt=1;
    while(ttt--) MAIN();
    return 0;    
}
posted @ 2021-03-28 01:48  Iscream-2001  阅读(130)  评论(0编辑  收藏  举报
/* */ /* */