1. 程式人生 > >LOJ2269 [SDOI2017] 切樹遊戲 【FWT】【動態DP】【樹鏈剖分】【線段樹】

LOJ2269 [SDOI2017] 切樹遊戲 【FWT】【動態DP】【樹鏈剖分】【線段樹】

stack dep 根據 註意 樹形dp 沒有 top cto HERE

題目分析:

好題。本來是一道好的非套路題,但是不湊巧的是當年有一位國家集訓隊員正好介紹了這個算法。

首先考慮靜態的情況。這個的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 }

LOJ2269 [SDOI2017] 切樹遊戲 【FWT】【動態DP】【樹鏈剖分】【線段樹】