• P5445 [APIO2019]路灯


    传送门·

    对于询问 $(a,b)$ ,感觉一维很不好维护,考虑把询问看成平面上的一个点,坐标为 $(a,b)$

    每个坐标 $(x,y)$ 的值表示到当前 $x$ 和 $y$ 联通的时间和

    考虑一个修改的贡献,它其实就是把左边一段区间 $[l,x]$ 和右边一段区间 $[x+1,r]$ 联通或断开

    放到平面上发现其实就是横坐标在 $[l,x]$ ,纵坐标在 $[x+1,r]$ 的矩形里修改,那么矩形左下角为 $[l,x+1]$,右上角为 $[x,r]$

    如果每个时间点都把相应矩形 $+1$ 的话显然是不可行的,考虑起点和终点的时间差

    如果当前操作是联通,设当前时间为 $t$,则把相应矩形 $-t$,如果是断开则把矩形加 $t$

    这样我们询问时直接矩形单点查值即可,但是要注意,如果查询时当前区间仍然联通,那么对应矩形还没加 $t$ ,我们查完值以后答案还要再加 $t$

    矩形加具体就是差分成四个前缀修改,查询的时候直接查左下所有位置的和即可

    如果把左下角为 $(xa,ya)$,右上角 $(xb,yb)$ 的闭区间都加一个 $v$,那么其实就是把 $(0,0),(xa,ya)$ 加一个 $v$,$(0,0),(xa,yb+1)$ 减 $v$,$(0,0),(xb+1,ya)$ 减 $v$,$(0,0),(xb+1,yb+1)$ 加 $v$

    二维平面加一维时间轴, $CDQ$ 分治即可

    然后就是具体的维护了,我维护亮灯区间用的是闭区间,好像比较多细节...

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<set>
    using namespace std;
    typedef long long ll;
    inline 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<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=6e5+7;
    char s[N];
    int n,Q,tot;
    struct dat{//区间
        int l,r;
        dat (int a=0,int b=0) { l=a,r=b; }
        inline bool operator < (const dat &tmp) const {
            return l<tmp.l;
        }
    };
    set <dat> S;//维护开灯的区间
    set <dat>::iterator ita,itb;
    struct Poi{//CDQ分治的点
        int x,y,id,v;//id=0表示是修改,v是修改的值
        Poi (int a=0,int b=0,int c=0,int d=0) { x=a,y=b,id=c,v=d; }
    }d[N<<2],tmp[N<<2];
    int t[N];
    inline void add(int x,int v) { while(x<=n+1) t[x]+=v,x+=x&-x; }
    inline int ask(int x) { int res=0; while(x) res+=t[x],x-=x&-x; return res; }
    inline bool fc(Poi &a,Poi &b) { if(a.x!=b.x) return a.x<b.x; return a.y!=b.y ? a.y<b.y : a.id<b.id; }
    int ans[N];
    void solve(int l,int r)
    {
        if(l==r) return; int mid=l+r>>1;
        solve(l,mid); solve(mid+1,r);
        int i=l,j=mid+1,p=l-1;
        while(i<=mid&&j<=r)
        {
            if(fc(d[i],d[j]))
            {
                if(!d[i].id) add(d[i].y,d[i].v);
                tmp[++p]=d[i++]; continue;
            }
            if(d[j].id) ans[d[j].id]+=ask(d[j].y);
            tmp[++p]=d[j++];
        }
        while(i<=mid) { if(!d[i].id) add(d[i].y,d[i].v); tmp[++p]=d[i++];  }
        while(j<=r) { if(d[j].id) ans[d[j].id]+=ask(d[j].y); tmp[++p]=d[j++]; }
        for(int k=l;k<=mid;k++) if(!d[k].id) add(d[k].y,-d[k].v);//撤销
        for(int k=l;k<=r;k++) d[k]=tmp[k];
    }
    int main()
    {
        n=read(),Q=read();
        scanf("%s",s+1);
        S.insert(dat(0,0)); S.insert(dat(n+1,n+1));//初始虚区间防越界
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='0') continue;
            int R=i; while(s[R+1]=='1') R++;
            S.insert(dat(i,R)); i=R;//初始情况
        }
        char ch[17]; int a,b,cnt=0;
        for(int i=1;i<=Q;i++)
        {
            scanf("%s",ch); a=read();
            if(ch[0]=='q')
            {
                b=read();
                d[++tot]=Poi(a,b,++cnt,0);//询问点
                ita=S.lower_bound(dat(a+1,0)); ita--;
                itb=S.lower_bound(dat(b,0)); itb--;
                if( ita==itb && (*ita).r>=b-1 ) ans[cnt]+=i;//判断区间是否还联通
                continue;
            }
            if(s[a]=='0')//联通
            {
                ita=S.lower_bound(dat(a+1,0)); ita--; itb=ita; ita++;//找到左右区间
                dat A=(*ita),B=(*itb); int l=a,r=a;
                if(B.r==l-1&&B.l>=1) l=B.l,S.erase(B);//注意判断,注意边界
                if(A.l==r+1&&A.r<=n) r=A.r,S.erase(A);
                S.insert(dat(l,r));
                d[++tot]=Poi(l,a+1,0,-i); d[++tot]=Poi(a+1,r+2,0,-i);
                d[++tot]=Poi(l,r+2,0,i); d[++tot]=Poi(a+1,a,0,i);
                //联通时矩形-i
                s[a]='1'; continue;
            }
            //断开
            ita=S.lower_bound(dat(a+1,0)); ita--; dat A=(*ita);
            S.erase(A); if(A.l<a) S.insert(dat(A.l,a-1)); if(A.r>a) S.insert(dat(a+1,A.r));//注意边界
            d[++tot]=Poi(A.l,a+1,0,i); d[++tot]=Poi(a+1,A.r+2,0,i);
            d[++tot]=Poi(A.l,A.r+2,0,-i); d[++tot]=Poi(a+1,a,0,-i);
            //断开时矩形+i
            s[a]='0';
        }
        solve(1,tot);
        for(int i=1;i<=cnt;i++) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    【提高组】
    【学习】数论
    【2019.10.2】NOIP2018 模拟赛
    【普及组BOSS】
    ELK搭建elasticsearch常见报错
    Linux 下 安装Python第三方模块工具箱pip,以及用pip安装的方法
    Centos 基本命令不能用恢复方法
    Docker0 网卡删除
    Tomcat 设置开机自启
    Python 终端输出字体颜色
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11371368.html
Copyright © 2020-2023  润新知