From: ouuan Date: Sat, 30 Mar 2019 11:38:53 +0000 (+0800) Subject: Update tree-decompose.md X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=f4cf270c52f2806cab1a8056b89e1850e1908885;p=oi-wiki%2Fmain.git Update tree-decompose.md --- diff --git a/docs/ds/tree-decompose.md b/docs/ds/tree-decompose.md index 8b137891..cea3bfb5 100644 --- a/docs/ds/tree-decompose.md +++ b/docs/ds/tree-decompose.md @@ -1 +1,301 @@ +# 分块方式 +关于树分块,可以先看一道模板题 [[SCOI2005]王室联邦](https://www.luogu.org/problemnew/show/P2325)。 + +这道题所要求的方式就是树分块的方式。 + +那么如何满足**每块大小在 $[B,3B]$,块内每个点到核心点路径上的所有点都在块内**呢? + +这里先提供一种构造方式,再予以证明: + +**dfs,并创建一个栈,dfs一个点时先记录初始栈顶高度,每dfs完当前节点的一棵子树就判断栈内(相对于刚开始dfs时)新增节点的数量是否≥B,是则将栈内所有新增点分为同一块,核心点为当前dfs的点,当前节点结束dfs时将当前节点入栈,整个dfs结束后将栈内所有剩余节点归入已经分好的最后一个块。** + +参考代码: + +```cpp +void dfs(int u,int fa) +{ + int t=top; + for (int i=head[u];i;i=nxt[i]) + { + int v=to[i]; + if (v!=fa) + { + dfs(v,u); + if (top-t>=B) + { + ++tot; + while (top>t) bl[sta[top--]]=tot; + } + } + } + sta[++top]=u; +} + +int main() +{ + //....... + + dfs(1,0); + + while (top) bl[sta[top--]]=tot; +} +``` + +如果你看懂了这个方法的话,每块大小≥B是显然的,下面证明为何每块大小≤3B: + +对于当前节点的每一棵子树: + +- 若未被分块的节点数≥B,那么在dfs这棵子树的根节点时就一定会把这棵子树的一部分分为一块直至这棵子树的剩余节点数≤B,所以这种情况不存在。 +- 若未被分块的节点数=B,这些节点一定会和栈中所有节点分为一块,栈中之前还剩 $[0,B-1]$ 个节点,那么这一块的大小为 $[B,2B-1]$ 。 +- 若未被分块的节点数 +#include +#include +#include + +using namespace std; + +const int N=100010; + +void pathmodify(int u,int v); //将T(u,v)取反并更新答案 +void opp(int x); //将节点x取反并更新答案 +void modify(int ti); //进行或回退修改ti +int lca(int u,int v); +void dfs(int u); //进行分块并记录dep数组、f数组(用于求lca、两点间路径) +void add(int u,int v); + +int head[N],nxt[N<<1],to[N<<1],cnt; +int n,m,Q,B,bl[N],tot,V[N],W[N],a[N],sta[N],top,qcnt,ccnt,dep[N],f[20][N],num[N],now; +long long ans,out[N]; +bool vis[N]; + +struct Query +{ + int u,v,t,id; + bool operator<(Query& y) + { + return bl[u]==bl[y.u]?(bl[v]==bl[y.v]?t>n>>m>>Q; + B=pow(n,0.666); + + for (i=1;i<=m;++i) cin>>V[i]; + for (i=1;i<=n;++i) cin>>W[i]; + + for (i=1;i>u>>v; + add(u,v); + add(v,u); + } + + dfs(1); + + for (i=1;i<=16;++i) + { + for (j=1;j<=n;++j) + { + f[i][j]=f[i-1][f[i-1][j]]; + } + } + + while (top) bl[sta[top--]]=tot; + + for (i=1;i<=n;++i) cin>>a[i]; + + for (i=0;i>type; + if (type==0) + { + ++ccnt; + cin>>c[ccnt].p>>c[ccnt].x; + } + else + { + cin>>q[qcnt].u>>q[qcnt].v; + q[qcnt].t=ccnt; + q[qcnt].id=qcnt; + ++qcnt; + } + } + + sort(q,q+qcnt); + + u=v=1; + + for (i=0;iq[i].t) modify(now--); + lc=lca(u,v); + opp(lc); + out[q[i].id]=ans; + opp(lc); + } + + for (i=0;idep[v]) + { + opp(u); + u=f[0][u]; + } + while (u!=v) + { + opp(u); + opp(v); + u=f[0][u]; + v=f[0][v]; + } +} + +void opp(int x) +{ + if (vis[x]) ans-=1ll*V[a[x]]*W[num[a[x]]--]; + else ans+=1ll*V[a[x]]*W[++num[a[x]]]; + vis[x]^=1; +} + +void modify(int ti) +{ + if (vis[c[ti].p]) + { + opp(c[ti].p); + swap(a[c[ti].p],c[ti].x); + opp(c[ti].p); + } + else swap(a[c[ti].p],c[ti].x); +} + +int lca(int u,int v) +{ + if (dep[u]=0;--i) + { + if (f[i][u]!=f[i][v]) + { + u=f[i][u]; + v=f[i][v]; + } + } + return f[0][u]; +} + +void dfs(int u) +{ + int t=top; + for (int i=head[u];i;i=nxt[i]) + { + int v=to[i]; + if (v!=f[0][u]) + { + f[0][v]=u; + dep[v]=dep[u]+1; + dfs(v); + if (top-t>=B) + { + ++tot; + while (top>t) bl[sta[top--]]=tot; + } + } + } + sta[++top]=u; +} + +void add(int u,int v) +{ + nxt[++cnt]=head[u]; + head[u]=cnt; + to[cnt]=v; +} +``` + +## 其它树分块 + +在模拟赛遇到过一道题,并没有在线提交的地方,也找不到代码了.. + +题意大概是:给你一棵点有颜色的树,每次询问给你若干条路径,要么询问这些路径的并的颜色数,要么询问这些路径的并出现的颜色的 mex(最小的未出现颜色),强制在线。 + +std 是树分块+bitset:预处理出块的关键点之间路径上颜色的 bitset,询问的时候块内暴力,块直接已经预处理了。