洛谷P5280 [ZJOI2019]线段树
https://www.luogu.org/problemnew/show/P5280
省选的时候后一半时间开这题,想了接近两个小时的各种假做法,之后想的做法已经接近正解了,但是有一些细节问题理不清楚(事实证明出来后再给我2个小时也还是没理清楚,只能说自己naive),而且也码不完,打了个20分暴力
参考资料:题解
首先,可以分开考虑各个点
对于每个点,考虑对于t次修改,每一次标记为启用或不启用(共有$2^t$种标记方案),其中有多少种标记方案使得这个点最后有tag
询问的答案就是每个点的答案之和
对于每个点:
f[i]表示i点自身有tag的方案数
g[i]表示i或其祖先有tag的方案数(状态里必须是i或其祖先,如果只是祖先(或者只是父亲)就没法做了,以前卡在这个坑里了...)
设这一次是第k次修改
考虑根到某个节点u的链a1,a2,a3,..,at,u
可以发现,这次修改对这个节点的影响分5类:(具体可以参考上面的“参考资料”)
如果"把a1,a2,..,at之一节点直接打tag"(对应参考资料里第4类),则f[u]*=2,g[u]+=$2^{k-1}$
如果"把u直接打tag"(对应第2类),则f[u]+=$2^{k-1}$,g[u]+=$2^{k-1}$
如果"删掉a1,a2,..,at,u的全部tag"(对应第1类),则(啥也没)
如果"删掉a1,a2,..,at的全部tag,如果删掉了至少1个tag就让u加上tag"(对应第3类),则f[u]+=g[u],g[u]*=2
如果"删掉a1,a2,..,ap(p<t)的全部tag(如果删掉了至少1个tag就让a[p+1]加上tag)"(对应第5类),则f[u]*=2,g[u]*=2
这一堆东西是可以用线段树直接维护的,其中第1,2,3类的直接暴力修改,4,5类用懒标记
可以把方案数转换为概率,相当于每次做以上修改时加上的常数都要除以$2^{k-1}$,做完以上修改后再将所有点f和g都除以2,这样可以少记标记,减小常数
这样的话,第4类就是g[u]=g[u]/2+1/2,第5类就是啥也没,剩下几类不需要lazytag比较容易,因此可以只记g的加法和乘法tag
剩下几类:第1类f[u]/=2,g[u]/=2,第2类f[u]=(f[u]+1)/2,g[u]=(g[u]+1)/2,第3类f[u]=(f[u]+g[u])/2
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 const int md=998244353; 13 #define addto(a,b) ((a)+=(b),((a)>=md)&&((a)-=md)) 14 #define multo(a,b) ((a)=ull(a)*(b)%md) 15 namespace S 16 { 17 struct Nd 18 { 19 int f,g,addg,mulg; 20 int sumf; 21 }d[400011]; 22 #define LC (u<<1) 23 #define RC (u<<1|1) 24 inline void upd(int l,int r,int u) 25 { 26 d[u].sumf=d[u].f; 27 if(l==r) return; 28 addto(d[u].sumf,d[LC].sumf); 29 addto(d[u].sumf,d[RC].sumf); 30 } 31 inline void doAddg(int u,int x) 32 { 33 addto(d[u].g,x); 34 addto(d[u].addg,x); 35 } 36 inline void doMulg(int u,int x) 37 { 38 multo(d[u].g,x); 39 multo(d[u].addg,x); 40 multo(d[u].mulg,x); 41 } 42 inline void pd(int l,int r,int u) 43 { 44 if(l==r) return; 45 if(d[u].mulg!=1) 46 { 47 doMulg(LC,d[u].mulg); 48 doMulg(RC,d[u].mulg); 49 d[u].mulg=1; 50 } 51 if(d[u].addg) 52 { 53 doAddg(LC,d[u].addg); 54 doAddg(RC,d[u].addg); 55 d[u].addg=0; 56 } 57 } 58 void build(int l,int r,int u) 59 { 60 if(l==r) 61 { 62 d[u].mulg=1; 63 return; 64 } 65 int mid=(l+r)>>1; 66 build(l,mid,LC);build(mid+1,r,RC); 67 d[u].mulg=1; 68 } 69 void setx(int L,int R,int l,int r,int u)//X=2^{k-2} 70 { 71 pd(l,r,u); 72 if(L<=l && r<=R) 73 { 74 //4类,u的任意后代节点 75 multo(d[u].addg,499122177); 76 multo(d[u].mulg,499122177); 77 addto(d[u].addg,499122177); 78 //2类,u自身 79 d[u].f=ull(d[u].f+1)*499122177%md; 80 d[u].g=ull(d[u].g+1)*499122177%md; 81 upd(l,r,u); 82 return; 83 } 84 int mid=(l+r)>>1; 85 if(L<=mid && mid<R) 86 { 87 setx(L,R,l,mid,LC); 88 setx(L,R,mid+1,r,RC); 89 } 90 else if(L<=mid) 91 { 92 setx(L,R,l,mid,LC); 93 //3类,RC 94 pd(mid+1,r,RC); 95 d[RC].f=ull(d[RC].f+d[RC].g)*499122177%md; 96 upd(mid+1,r,RC); 97 //5类,RC的任意后代节点(啥也不干) 98 } 99 else if(mid<R) 100 { 101 setx(L,R,mid+1,r,RC); 102 //3类,LC 103 pd(l,mid,LC); 104 d[LC].f=ull(d[LC].f+d[LC].g)*499122177%md; 105 upd(l,mid,LC); 106 //5类,LC的任意后代节点(啥也不干) 107 } 108 //1类,u自身 109 multo(d[u].f,499122177); 110 multo(d[u].g,499122177); 111 upd(l,r,u); 112 } 113 } 114 int pw2[200011];//pw2[i]=2^{i-2} 115 int n,m,mm; 116 int main() 117 { 118 int i,idx,l,r; 119 pw2[1]=499122177; 120 for(i=2;i<=200000;++i) 121 pw2[i]=ull(pw2[i-1])*2%md; 122 scanf("%d%d",&n,&m); 123 S::build(1,n,1); 124 while(m--) 125 { 126 scanf("%d",&idx); 127 if(idx==1) 128 { 129 scanf("%d%d",&l,&r); 130 ++mm; 131 S::setx(l,r,1,n,1); 132 } 133 else 134 { 135 printf("%llu\n",ull(S::d[1].sumf)*pw2[mm+2]%md); 136 } 137 } 138 return 0; 139 }