zoukankan      html  css  js  c++  java
  • 【洛谷5445】[APIO2019] 路灯(树套树)

    点此看题面

    大致题意:(n)个点,规定(x,y)连通当且仅当(a_x=a_{x+1}=...=a_y=1)。给定零时刻(a_i)的值,每个时刻可能会发生两种事件:将(a_x)取反((0->1,1->0)),或询问(x,y)有多少个时刻连通。

    树套树

    这道题一眼树套树,然后随便推了推就推出来了,应该算是一道比较水的题目吧。

    考虑用平面上一点((x,y))表示(x,y)的答案。

    一个基本性质,如果(x,y)连通,则(x,y)之间的所有点都是连通的。

    于是我们可以抠出序列中每一整段全是(1)的区间,然后类似于(ODT)(set)进行维护,其中每个区间要维护它的诞生时间。

    每次改变一个点的值的时候,无非就是连通若干区间或是断开一个区间。

    无论是连通还是断开,都需要先计算原区间的贡献(用当前时间减去区间诞生时间)并从(set)中删去,然后往(set)中加入新的区间。

    对于([l,r])区间,它的贡献范围就是左上角为((l,l))、右下角为((r,r))的一个矩形。

    因此只要区间修改、单点查询,树状数组套线段树即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    using namespace std;
    int n,a[N+5];char s[N+5];
    struct V//存储一个区间的信息
    {
    	int l,r,t;I V(CI a=0,CI b=0,CI c=0):l(a),r(b),t(c){}//l,r为左右端点,t为诞生时间
    	I bool operator < (Con V& o) Con {return l<o.l;}
    };set<V> S;typedef set<V>::iterator IT;//用set维护
    class SegmentArray//树状数组套线段树
    {
    	private:
    		int Rt[N+5];class SegmentTree//动态开点线段树
    		{
    			private:
    				int Nt;struct node {int V,S[2];}O[N*300];
    			public:
    				I void U(int& rt,CI L,CI R,CI v,CI l=1,CI r=n)//区间修改
    				{
    					if(!rt&&(rt=++Nt),L<=l&&r<=R) return (void)(O[rt].V+=v);RI mid=l+r>>1;
    					L<=mid&&(U(O[rt].S[0],L,R,v,l,mid),0),R>mid&&(U(O[rt].S[1],L,R,v,mid+1,r),0);
    				}
    				I int Q(int& rt,CI p,CI l=1,CI r=n)//单点查询
    				{
    					if(!rt||l==r) return O[rt].V;RI mid=l+r>>1;
    					return (p<=mid?Q(O[rt].S[0],p,l,mid):Q(O[rt].S[1],p,mid+1,r))+O[rt].V;
    				}
    		}S;
    		I void U(RI x,CI l,CI r,CI v) {W(x<=n) S.U(Rt[x],l,r,v),x+=x&-x;}
    	public:
    		I void U(CI l,CI r,CI v) {U(l,l,r,v),U(r+1,l,r,-v);}//区间修改
    		I int Q(RI x,CI y,RI t=0) {W(x) t+=S.Q(Rt[x],y),x-=x&-x;return t;}//单点查询
    }T;
    #define Find(x) --S.upper_bound(x)//在set中找到对应区间
    I void On(CI ti,CI x)//打开
    {
    	IT v;RI l=x,r=x;a[x+1]&&(v=Find(x+1),T.U(v->l,v->r,ti-v->t),r=v->r,S.erase(v),0),//若右边存在区间,计算贡献并删去
    	a[x-1]&&(v=Find(x-1),T.U(v->l,v->r,ti-v->t),l=v->l,S.erase(v),0),S.insert(V(l,r,ti));//若左边存在区间,计算贡献并删去;最后加入新区间
    }
    I void Off(CI ti,CI x)//关掉
    {
    	IT v=Find(x);RI l=v->l,r=v->r;T.U(l,r,ti-v->t),S.erase(v),//计算贡献并删去
    	l^x&&(S.insert(V(l,x-1,ti)),0),r^x&&(S.insert(V(x+1,r,ti)),0);//断成两个新区间
    }
    I int Ask(CI ti,CI x,CI y)//求出尚未统计的答案
    {
    	if(!a[x]) return 0;IT v=Find(x);return v->r>=y?ti-v->t:0;//如果在同一连通块中才有贡献
    }
    int main()
    {
    	RI Qt,i,j;for(scanf("%d%d%s",&n,&Qt,s+1),i=1;i<=n;++i) a[i]=s[i]&1;//读入数据
    	for(i=1;i<=n;i=j+1) if(a[j=i]) {W(j^n&&a[j+1]) ++j;S.insert(V(i,j,0));}//初始化抠区间
    	RI x,y;for(i=1;i<=Qt;++i) switch(scanf("%s%d",s+1,&x),s[1])//处理操作
    	{
    		case 't':(a[x]^=1)?On(i,x):Off(i,x);break;//修改
    		case 'q':scanf("%d",&y),--y,printf("%d
    ",T.Q(x,y)+Ask(i,x,y));break;//询问
    	}return 0;
    }
    
  • 相关阅读:
    tensorflow2.0 GPU和CPU 时间对比
    第一次使用FileZilla Server
    PremiumSoft Navicat 15 for Oracle中文破解版安装教程
    Unmapped Spring configuration files found. Please configure Spring facet or use 'Create Default Context' to add one including all unmapped files.
    ng : 无法加载文件 D: odejs ode_global g.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
    angular
    Github上优秀的go项目
    win10---file explore 中remove quick access folder
    react--useEffect使用
    linux---cat 和 grep 的妙用
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5445.html
Copyright © 2011-2022 走看看