zoukankan      html  css  js  c++  java
  • 【LOJ#3043】【洛谷P5280】【ZJOI2019】—线段树(计数dp+线段树)

    LOJ传送门

    洛谷传送门

    早上在知乎看到吉司机说这是一道期望+数据结构
    一脸懵逼

    原来就是把计数看成期望乘上情况


    分析一波复制操作
    就可以发现其实就是求每次操作有//没有的2t2^t种情况的tagtag之和

    如果按照线段树这个样子
    似乎……可以直接用线段树维护?
    维护一个tr[u]tr[u]表示线段树上点uu当前有多少种情况为11

    没有影响到的点显然tagtag是不会改变的的,也就是trtr乘2

    对于所有线段树遍历而且非修改的点,他们的tagtag显然都被变成00
    所以这些点trtr不变

    对于所有直接修改到的节点显然无论前面情况怎么样现在的tagtag都为1
    也就是加上2t2^t

    发现pushdownpushdown还会影响所有修改节点的兄弟
    但是只有当原来父亲以上有tagtag为1的时候这里才会变成1
    再维护一个f[u]f[u]表示有多少种情况f[u]f[u]到根会有点tagtag为1
    trtr直接加上ff就是了

    那考虑ff怎么维护
    对于不被修改的点,tagtag不变,ff乘2

    修改路径上的点,tagtag变成了0,ff不变

    被修改的点,tagtag变成了11ff加上2t2^t

    所有兄弟,tagtag变成了11ff乘2

    又发现所有不被修改的点都是兄弟的儿子
    所以就只用讨论3种情况了

    修改路径上的点:trtr不变,ff不变

    被修改的点:tr+=2ttr+=2^t,子树所有f+=2tf+=2^t

    兄弟节点:子树所有tr+=ftr+=f,子树所有f=f2f=f*2

    #include<bits/stdc++.h>
    using namespace std;
    const int RLEN=1<<20|1;
    inline char gc(){
    	static char ibuf[RLEN],*ib,*ob;
    	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    	return (ib==ob)?EOF:*ib++;
    }
    inline int read(){
    	char ch=gc();
    	int res=0,f=1;
    	while(!isdigit(ch))f^=ch=='-',ch=gc();
    	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    	return f?res:-res;
    }
    const int N=100005;
    const int mod=998244353;
    inline int add(int a,int b){
    	return a+b>=mod?a+b-mod:a+b;
    }
    inline int mul(int a,int b){
    	return 1ll*a*b>=mod?1ll*a*b%mod:a*b;
    }
    inline void dec(int &a,int b){
    	a=a>=b?a-b:a-b+mod;
    }
    inline void selfadd(int &a,int b){
    	a=add(a,b);
    }
    inline void selfmul(int &a,int b){
    	a=mul(a,b);
    }
    int ans,t,n,m,res;
    namespace Seg{
    	int tr[N<<2],f[N<<2],mulf[N<<2],addf[N<<2],mul[N<<2];
    	#define lc (u<<1)
    	#define rc ((u<<1)|1)
    	#define mid ((l+r)>>1)
    	inline void build(int u,int l,int r){
    		mulf[u]=mul[u]=1;
    		if(l==r)return;
    		build(lc,l,mid),build(rc,mid+1,r);
    	}
    	inline void pushmulf(int u,int k){
    		selfmul(addf[u],k),selfmul(f[u],k),selfmul(mulf[u],k);
    	}
    	inline void pushadd(int u,int k){
    		selfadd(f[u],k),selfadd(addf[u],k);
    	}
    	inline void pushmul(int u,int k){
    		selfmul(mul[u],k),selfmul(tr[u],k);
    	}
    	inline void pushdown(int u){
    		if(mulf[u])pushmulf(lc,mulf[u]),pushmulf(rc,mulf[u]),mulf[u]=1;
    		if(addf[u])pushadd(lc,addf[u]),pushadd(rc,addf[u]),addf[u]=0;
    		if(mul[u])pushmul(lc,mul[u]),pushmul(rc,mul[u]),mul[u]=1;
    	}
    	void update(int u,int l,int r,int st,int des){
    		if(st<=l&&r<=des){
    			dec(ans,tr[u]);
    			selfadd(tr[u],t),pushadd(u,t),selfmul(mul[u],2);
    			selfadd(res,tr[u]);
    			return;
    		}
    		if(r<st||des<l){
    			dec(ans,tr[u]);
    			selfadd(tr[u],f[u]),selfmul(mul[u],2),pushmulf(u,2);
    			selfadd(res,tr[u]);
    			return;
    		}
    		pushdown(u);
    		dec(ans,tr[u]),selfadd(res,tr[u]);
    		update(lc,l,mid,st,des);
    		update(rc,mid+1,r,st,des);
    	}
    }
    using namespace Seg;
    int main(){
    	n=read(),m=read();
    	build(1,1,n);t=1;
    	for(int i=1;i<=m;i++){
    		int op=read();
    		if(op==1){
    			int l=read(),r=read();
    			res=0;
    			update(1,1,n,l,r);
    			selfmul(t,2);
    			selfmul(ans,2);
    			selfadd(ans,res);
    		}
    		else cout<<ans<<'
    ';
    	}
    }
    
  • 相关阅读:
    【ARM-Linux开发】用VS2013+VELT-0.1.4进行海思平台 Linux内核 的开发
    【CUDA开发-并行计算】NVIDIA深度学习应用之五大杀器
    [ARM-Linux开发]Linux open函数
    [ARM-Linux开发]mknod命令使用
    [ARM-LInux开发]linux设备驱动makefile入门解析
    [ARM-Linux开发] 嵌入式 linux如何生成ko文件
    [ARM-Linux开发]Linux下加载.ko驱动模块的两种方法:insmod与modprobe
    [ARM-Linux开发] 主设备号--驱动模块与设备节点联系的纽带
    【视频开发】Gstreamer框架中使用gst-launch进行流媒体播放
    【视频开发】用GStreamer实现摄像头的采集和保存
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/11145565.html
Copyright © 2011-2022 走看看