洛谷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 }
View Code

 

posted @ 2019-04-04 08:08  hehe_54321  阅读(175)  评论(0编辑  收藏  举报
AmazingCounters.com