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 }