链接
https://vjudge.net/problem/SPOJ-QTREE6
题解
写得我脑壳疼,参考了不少题解,加深了对树剖的理解
用 W[u]维护当前局势下,如果 u为白色, u为根的子树中和 u联通的个数
用 B[u]维护当前局势下,如果 u为黑色, u为根的子树中和 u联通的个数
那么,对于询问操作,向上找到最远的与 u同色的节点 x,那么 W/B[x]就是答案
然后是修改操作
不妨设将u的颜色从白改成黑
从 u的父节点往上走,走到第一个黑点 v1,设路径 path1为 fa[u]→v1,然后将 path1上所有点的 W值减去 W[u]。
同样地,从 u的父节点往上走,走到第一个白点 v2,设路径 path2为 fa[u]→v2,然后将 path2上所有点的 B值加上 B[u]。
上面这些很好理解,只剩下两个问题,如果向上查找“最远的同色”和“最近的异***r> 首先,“最远的同色”可以转化为“最近的异色”沿着原路径向下一步
所以,只要解决“最近的异色”即可
0表示白色,1表示黑***r> 树链剖分是一段一段向上的,如果整段是白色,和必为0,如果整段是黑色,和必为线段的长度
利用上述性质向上找到目标点所在的区间后,再在区间中二分查找即可
下面是选择数据结构
在解决“最近的异色”中,用到了区间求和,然后题目要求是单点修改,所以树状数组可以解决
在修改操作中,需要整段修改,但是答案是单点查询,所以树状数组可以解决
综上,需要3个树状数组就可以解决
注意的问题
在整段的修改 path过程中,要利用树剖的性质,一段一段修改!!!
所以修改的复杂度为 O(log2n)
总复杂度为 O(n∗log2n)
代码
#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 31592653589793
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
vector<int> a[N];
int n,m,cnt,rt,tg,pre,son[N],top[N],id[N],fa[N],d[N],rk[N],sz[N],g[3][N],c[N];
void dfs1(int x,int ffa){
sz[x]=1;
for(auto i:a[x]) if (i!=ffa){
fa[i]=x; d[i]=d[x]+1; dfs1(i,x); sz[x]+=sz[i];
if (sz[i]>sz[son[x]]) son[x]=i;
}
}
void dfs2(int x,int t){
top[x]=t; id[x]=++cnt; rk[cnt]=x;
if (!son[x]) return;
dfs2(son[x],t);
for (auto i:a[x]) if (i!=son[x] && i!=fa[x]) dfs2(i,i);
}
inline int cal(int *a,int x){
int res=0; for (int i=x;i>0;i-=i&-i) res+=a[i]; return res;
}
inline void up(int *a,int x,int d){
for (int i=x;i<N;i+=i&-i) a[i]+=d;
}
inline void upseg(int *a,int x,int y,int d){
while(top[x]!=top[y]){
up(a,id[top[y]],d); up(a,id[y]+1,-d);
y=fa[top[y]];
}
up(a,id[x],d);up(a,id[y]+1,-d);
}
inline int sumseg(int *a,int x,int y){
return cal(a,y)-cal(a,x-1);
}
int find(int x,int fg){
int l,r,ans; fg^=1;
while(x){
int t=sumseg(g[2],id[top[x]],id[x]);
if (t!=(id[x]-id[top[x]]+1)*fg)break;
pre=id[top[x]];
x=fa[top[x]];
}
if (!x) return 1;
l=id[top[x]]; r=id[x];
while(l<=r){
int t=l+r>>1;
int tm=sumseg(g[2],t,r);
if (tm!=(r-t+1)*fg) ans=t,l=t+1;else r=t-1;
}
if (ans!=id[x]) pre=ans+1;
return rk[ans];
}
int main(){
sc(n);
for (int i=1,x,y;i<n;i++){
scc(x,y);
a[x].pb(y),a[y].pb(x);
}
dfs1(1,-1);
dfs2(1,1);
for (int i=1;i<=n;i++) upseg(g[0],i,i,sz[i]),upseg(g[1],i,i,1);
sc(m);
while(m--){
int op,x,v;
scc(op,x);
if (op){
int w=cal(g[0],id[x]),b=cal(g[1],id[x]);
if (c[x]){
if (x>1){
v=find(fa[x],0); upseg(g[1],v,fa[x],-b);
v=find(fa[x],1); upseg(g[0],v,fa[x],w);
}
up(g[2],id[x],-1);
}else{
if (x>1){
v=find(fa[x],1); upseg(g[0],v,fa[x],-w);
v=find(fa[x],0); upseg(g[1],v,fa[x],b);
}
up(g[2],id[x],1);
}
c[x]^=1;
}else{
find(x,c[x]^1);
printf("%d\n",cal(g[c[x]],pre));
}
}
}