LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

题目分析:

好题。本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法。 

首先考虑静态的情况。这个的DP方程非常容易写出来。

接着可以注意到对于异或结果的计数可以看成一个FWT的过程,进一步地可以注意到FWT在中途没有还原的必要。从FWT的过程中我们可以发现FWT具有可加性和交换律结合律。

这样原问题可以在静态的情况下通过树形DP做到$O(nm)$。

考虑动态的问题。根据《神奇的子图》命题报告及其拓展中描述的算法五,我们应该不难想到基于树链剖分的这样的做法。

首先对树进行轻重路径剖分,构建一棵重链树。接着将树中轻子树的影响放进对应的重链父节点(不是top节点)上。接着在线段树上合并问题的答案。

对于一个线段树中的结点,经过分析我们应该可以知道需要记录的信息有这么几个:

L:从这个线段树区间所对应的子重链的顶端开始延伸出的包含轻边子树的所有情况的FWT值。

R:从这个线段树区间所对应的子重链的底端开始延伸出的包含轻边子树的所有情况的FWT值。

C:这个线段树区间所对应的子重链上的所有点都被选择,接着往子树方向延伸的所有情况的FWT值。

tot:这个线段树区间所对应的子重链以及它的所有轻边子树看作一个整体的树的答案的FWT值。 

有了这些信息之后线段树的合并答案就不难写出来了。我们用下标$0$表示左子树,下标$1$表示右子树。

$L = L_0+C_0*L_1$

$R = R_1+C_1*R_0$

$C = C_0*C_1$

$tot = tot_0+tot_1+L_1*R_0$

我们还提到了线段树中的叶子结点是包含它的所有轻边子树信息的。根据《神奇的子图》命题报告及其拓展中的描述,我们能够想到通过普通的DP来转移答案。

下面我们用H来表示它的一个轻边儿子,Now表示叶子对应的原树上的点,Base为128个单位FWT之一。注意到对于叶子结点,他的L和R和C是相同的。所以忽略R和C。这样叶子的转移可以这样写:

$Leaf_L = Base[v[Now]]*\prod_{i \in son}(H_L+Base[0])$可以理解为01背包的选和不选。

$Leaf_{tot} = \sum_{i \in son}tot_i + Leaf_L$. 

 有了它们,我们现在可以进行修改了。

对于一次修改,我们要做的是,沿着重链树往上跳,遇到一条重链则更改重链在线段树中的信息。这样做只会经过$O(logn)$条重链,每条重链只会修改一个点,对应的在线段树上只会修改一个点,线段树每次修改时间复杂度为$O(logn)$,所以这是$O(log^2n)$的。

除以0的问题可以另外建一棵线段树解决,但大家好像都说可以记录0的个数,我不会,希望会的可以留个言。

时间复杂度$O(nmlog^2n)$,树链剖分常数较小,可以通过所有数据。

upd:LOJ时间倒数第三。。。

 

代码:

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int maxn = 30200;
  5 const int maxm = 128;
  6 const int mod = 10007;
  7 
  8 int n,m,num,num2,bfsnum;
  9 int v[maxn];
 10 vector <int> g[maxn];
 11 
 12 vector <int> Chain[maxn];
 13 
 14 int dep[maxn],fa[maxn],top[maxn],number[maxn],sz[maxn],son[maxn];
 15 int tail[maxn],where[maxn],im[maxn],bfsin[maxn],bfsout[maxn];
 16 
 17 struct func{
 18     int cont[maxm]; 
 19     func operator * (func b){
 20     for(int i=0;i<=maxm;i++) b.cont[i] = (b.cont[i]*cont[i])%mod;
 21     return b;
 22     }
 23     func operator + (func b){
 24     for(int i=0;i<=maxm;i++) b.cont[i] = (b.cont[i]+cont[i])%mod;
 25     return b;
 26     }
 27     func operator - (func b){
 28     for(int i=0;i<=maxm;i++) b.cont[i] = (cont[i]-b.cont[i]+mod)%mod;
 29     return b;
 30     }
 31 }base[maxm],T2[maxn<<2],Newnumber;
 32 
 33 struct node{
 34     func L,R,C,tot;
 35     //左端延伸,右端延伸,总和
 36 }T[maxn<<2];
 37 
 38 void push_up(int now){
 39     T[now].L = T[now<<1].C*T[now<<1|1].L + T[now<<1].L;
 40     T[now].R = T[now<<1|1].C*T[now<<1].R + T[now<<1|1].R;
 41     T[now].C = T[now<<1].C*T[now<<1|1].C;
 42     T[now].tot = T[now<<1].tot+T[now<<1|1].tot+T[now<<1].R*T[now<<1|1].L;
 43 }
 44 
 45 void dfs1(int now,int f,int dp){
 46     dep[now] = dp; fa[now] = f;
 47     for(int i=0;i<g[now].size();i++){
 48     if(g[now][i] == f) continue;
 49     dfs1(g[now][i],now,dp+1);
 50     sz[now] += sz[g[now][i]];
 51     if(sz[son[now]] < sz[g[now][i]]) son[now] = g[now][i];
 52     }
 53     sz[now]++;
 54 }
 55 
 56 void dfs2(int now,int tp){
 57     top[now] = tp; number[now] = ++num; where[num] = now;
 58     if(now == tp && now!=1){Chain[top[fa[now]]].push_back(now);}
 59     if(sz[now] != sz[son[now]] + 1){
 60     bfsin[now] = bfsnum+1;
 61     if(fa[now] != 0) bfsout[now] = bfsnum+g[now].size()-2;
 62     else bfsout[now] = bfsnum+g[now].size()-1;
 63     }else bfsin[now] = 1,bfsout[now] = -1;
 64     for(int i=0;i<g[now].size();i++){
 65     if(g[now][i] == fa[now] || g[now][i] == son[now]) continue;
 66     im[g[now][i]] = ++bfsnum;
 67     }
 68     if(son[now]) {dfs2(son[now],tp);tail[now]=tail[son[now]];}
 69     else tail[now] = now;
 70     for(int i=0;i<g[now].size();i++){
 71     if(g[now][i] == fa[now] || g[now][i] == son[now]) continue;
 72     dfs2(g[now][i],g[now][i]);
 73     }
 74 }
 75 
 76 node merge(node alpha,node beta){
 77     node gamma; gamma.L = alpha.L + beta.L*alpha.C;
 78     gamma.R = beta.R + alpha.R*beta.C;
 79     gamma.C = alpha.C*beta.C;
 80     gamma.tot = alpha.tot+beta.tot+alpha.R*beta.L;
 81     return gamma;
 82 }
 83 
 84 node Query(int now,int tl,int tr,int l,int r){
 85     if(tl >= l && tr <= r) return T[now];
 86     int mid = (tl+tr)/2;
 87     if(mid >= r) return Query(now<<1,tl,mid,l,r);
 88     if(mid < l) return Query(now<<1|1,mid+1,tr,l,r);
 89     node ans1 = Query(now<<1,tl,mid,l,r);
 90     node ans2 = Query(now<<1|1,mid+1,tr,l,r);
 91     return merge(ans1,ans2);
 92 }
 93 
 94 void build_tree(int now,int tl,int tr,int l,int r){
 95     if(tl > r || tr < l) return;
 96     if(tl == tr){
 97     tl = where[tl]; T[now].L = base[v[tl]];
 98     for(int i=0;i<g[tl].size();i++){
 99         if(g[tl][i] == fa[tl] || g[tl][i] == son[tl]) continue;
100         int SS = g[tl][i];
101         node forw = Query(1,1,n,number[SS],number[tail[SS]]);
102         T[now].tot = T[now].tot + forw.tot;
103         T[now].L = T[now].L*(base[0]+forw.L);
104     }
105     T[now].R = T[now].C = T[now].L;
106     T[now].tot = T[now].tot + T[now].L;
107     }else{
108     int mid = (tl+tr)/2;
109     build_tree(now<<1,tl,mid,l,r);
110     build_tree(now<<1|1,mid+1,tr,l,r);
111     push_up(now);
112     }
113 }
114 
115 void dfs3(int now){
116     for(int i=0;i<Chain[now].size();i++){ dfs3(Chain[now][i]); } //sub
117     int p = tail[now];
118     build_tree(1,1,n,number[now],number[tail[now]]);
119 }
120 
121 void read(){
122     scanf("%d%d",&n,&m);
123     for(int i=1;i<=n;i++) scanf("%d",&v[i]);
124     for(int i=1;i<n;i++){
125     int x,y; scanf("%d%d",&x,&y);
126     g[x].push_back(y); g[y].push_back(x);
127     }
128 }
129 
130 void FWT(func &now,int data){
131     now.cont[data] = 1;
132     for(int i=2;i<=maxm;i<<=1){
133     int yum = maxm/i;
134     for(int j=0;j<maxm;j+=yum*2){
135         for(int k=0;k<yum;k++){
136         int x = now.cont[j+k],y = now.cont[j+k+yum];
137         now.cont[j+k] = x+y; now.cont[j+k+yum] = (x-y+mod)%mod;
138         }
139     }
140     }
141 }
142 
143 void IFWT(func &now){
144     for(int i=1;i<maxm;i<<=1){
145     for(int j=0;j<=maxm;j+=i*2){
146         for(int k=0;k<i;k++){
147         int x = now.cont[j+k],y = now.cont[j+k+i];
148         now.cont[j+k] = ((x+y)*5004)%mod;
149         now.cont[j+k+i] = ((x-y+mod)*5004)%mod;
150         }
151     }
152     }
153 }
154 
155 func VQuery(int now,int tl,int tr,int l,int r){
156     if(tl >= l && tr <= r) return T2[now];
157     if(tl > r || tr < l) return base[0];
158     int mid = (tl+tr)/2;
159     return VQuery(now<<1,tl,mid,l,r)*VQuery(now<<1|1,mid+1,tr,l,r);
160 }
161 
162 void VModify(int now,int tl,int tr,int place){
163     if(tl == tr){
164     T2[now] = Newnumber;
165     }else{
166     int mid = (tl+tr)/2;
167     if(place <= mid) VModify(now<<1,tl,mid,place);
168     else VModify(now<<1|1,mid+1,tr,place);
169     T2[now] = T2[now<<1]*T2[now<<1|1];
170     }
171 }
172 
173 void Modify(int now,int tl,int tr,int place){
174     if(tl == tr){
175     tl = where[tl];
176     func res = VQuery(1,1,bfsnum,bfsin[tl],bfsout[tl]);
177     T[now].tot = T[now].tot - T[now].L;
178     T[now].L = T[now].R = T[now].C = res*base[v[tl]];
179     T[now].tot = T[now].tot + T[now].L;
180     }else{
181     int mid = (tl+tr)/2; 
182     if(place <= mid) Modify(now<<1,tl,mid,place);
183     else Modify(now<<1|1,mid+1,tr,place);
184     push_up(now);
185     }
186 }
187 
188 void Erase(int now,int tl,int tr,int place,int dr){
189     if(tl == tr){
190     tl = where[tl];
191     if(dr == 1)T[now].tot = T[now].tot - Newnumber;
192     else T[now].tot = T[now].tot + Newnumber;
193     }else{
194     int mid = (tl+tr)/2; 
195     if(place <= mid) Erase(now<<1,tl,mid,place,dr);
196     else Erase(now<<1|1,mid+1,tr,place,dr);
197     push_up(now);
198     }
199 }
200 
201 void work(){
202     for(int i=0;i<maxm;i++) FWT(base[i],i);
203     dfs1(1,0,1);
204     dfs2(1,1);
205     dfs3(1);
206     for(int i=1;i<=n;i++){
207     if(im[i]){
208         Newnumber=Query(1,1,n,number[i],number[tail[i]]).L + base[0];
209         VModify(1,1,bfsnum,im[i]);
210     }
211     }
212     int q; scanf("%d",&q);
213     for(int i=1;i<=q;i++){
214     char str[10]; scanf("%s",str);
215     if(str[0] == 'C'){
216         int x,y; scanf("%d%d",&x,&y);
217         int now = x;v[x] = y;
218         stack<int> sta;
219         while(fa[top[now]]!=0){sta.push(top[now]); now=fa[top[now]];}
220         while(!sta.empty()){ // clear tot
221         int hd = sta.top();sta.pop();
222         Newnumber = Query(1,1,n,number[hd],number[tail[hd]]).tot;
223         Erase(1,1,n,number[fa[hd]],1);
224         } now = x;
225         while(now != 0){
226         Modify(1,1,n,number[now]);
227         now = top[now];
228         Newnumber = Query(1,1,n,number[now],number[tail[now]]).tot;
229         if(fa[top[now]]) Erase(1,1,n,number[fa[now]],0);
230         Newnumber=base[0]+Query(1,1,n,number[now],number[tail[now]]).L;
231         if(im[now]) VModify(1,1,bfsnum,im[now]);
232         now = fa[now];
233         }
234     }else{
235         int k; scanf("%d",&k);
236         func ans = Query(1,1,n,number[1],number[tail[1]]).tot;
237         IFWT(ans);
238         printf("%d\n",ans.cont[k]);
239     }
240     }
241 }
242 
243 int main(){
244     read();
245     work();
246     return 0;
247 }

 

posted @ 2018-06-09 20:37  menhera  阅读(512)  评论(0编辑  收藏  举报