http://www.lydsy.com/JudgeOnline/problem.php?id=1095 (题目链接)
题意
一棵树,求最远的两黑点之间的距离,每次可以将黑点染白或者将白点染黑。
Solution
动态树分治,%%%重庆省选AK爷。
点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中“最高重心”上,从所有的重心处像分治中的不同子树索取到重心的链,就可以覆盖所有链的信息。动态点分治就像把序列分治变成线段树一样,在分治的架子上加了信息维护,实现树链信息维护与查询。
需要什么?
每个重心需要其每个分离子树到它的信息(很重要,否则形成链的重复部分,并且还需要一个自己到自己的空信息维护单链上来的信息)
每个重心需要它到父分治块的信息
全局需要维护每个重心的信息
因此,考虑问题的静态版本,需要维护每个节点每个子树内的最长链,那么为了修改,最大化应换成堆维护。
每个重心维护一个堆,表示其分离子树中每个到它的最长链
每个重心维护一个堆,表示其块内到父重心的最长链
全局维护一个堆,表示每个重心处的最长链
修改时只要从一个节点作为重心的块开始向上修改,就可以遍历到所有包含它的块,修改到父重心的信息,修改父重心的信息
注意开始时的节点的分离子树那个堆要插一个0表示单链,而改掉节点值的时候0的存在性也要相应地变化。
细节
今天我机子真是出了鬼了,几个明显会WA的错误竟然拍不出来,搞得我都怀疑自己存在的价值了→_→(虽然我也不知道这二者之间有什么联系)
代码
// bzoj1095 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #include<ctime> #define LL long long #define inf 1ll<<30 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=100010; int light[maxn],bin[30],par[maxn],head[maxn]; int n,Q; struct edge {int to,next;}e[maxn<<1]; struct hope { priority_queue<int> q,del; int top() { while (!del.empty() && q.top()==del.top()) q.pop(),del.pop(); return q.top(); } void pop() { while (!del.empty() && q.top()==del.top()) q.pop(),del.pop(); q.pop(); } int size() { return q.size()-del.size(); } void push(int x) { q.push(x); } void erase(int x) { del.push(x); } }q[maxn],d[maxn],ans; namespace LittleTrick { int cnt,deep[maxn],fa[maxn][30]; inline void link(int u,int v) { e[++cnt]=(edge){v,head[u]};head[u]=cnt; e[++cnt]=(edge){u,head[v]};head[v]=cnt; } inline void dfs(int x) { for (int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) { deep[e[i].to]=deep[x]+1; fa[e[i].to][0]=x; dfs(e[i].to); } } inline int lca(int x,int y) { if (deep[x]<deep[y]) swap(x,y); int t=deep[x]-deep[y]; for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i]; if (x==y) return x; for (int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } inline void insert(hope &a) { if (a.size()>1) { int tmp=a.top();a.pop(); ans.push(tmp+a.top());a.push(tmp); } } inline void erase(hope &a) { if (a.size()>1) { int tmp=a.top();a.pop(); ans.erase(tmp+a.top());a.push(tmp); } } inline int dis(int x,int y) { return deep[x]+deep[y]-2*deep[lca(x,y)]; } } using namespace LittleTrick; namespace NodeDivide { int Dargen,sum; int size[maxn],f[maxn],vis[maxn]; inline void caldargen(int x,int fa) { f[x]=0;size[x]=1; for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) { caldargen(e[i].to,x); size[x]+=size[e[i].to]; f[x]=max(f[x],size[e[i].to]); } f[x]=max(f[x],sum-size[x]); if (f[x]<f[Dargen]) Dargen=x; } inline void caldeep(int x,int fa,int p) { d[p].push(dis(x,par[p])); for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) caldeep(e[i].to,x,p); } inline void work(int x) { vis[x]=1; q[x].push(0); caldeep(x,0,x); for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) { sum=size[e[i].to];Dargen=0; caldargen(e[i].to,x); par[Dargen]=x;// par[e[i].to]=x; work(Dargen);//work(e[i].to); //q[x].push(d[e[i].to].top()); } q[par[x]].push(d[x].top()); insert(q[x]); } inline void Init() { f[Dargen=0]=inf;sum=n; caldargen(1,0); work(Dargen); } } using namespace NodeDivide; namespace Query { inline void On(int x) { erase(q[x]); q[x].erase(0); insert(q[x]); for (int i=x;par[i];i=par[i]) { erase(q[par[i]]); if (d[i].size()) q[par[i]].erase(d[i].top()); d[i].erase(dis(par[i],x)); if (d[i].size()) q[par[i]].push(d[i].top()); insert(q[par[i]]); } } inline void Off(int x) { erase(q[x]); q[x].push(0); insert(q[x]); for (int i=x;par[i];i=par[i]) { erase(q[par[i]]); if (d[i].size()) q[par[i]].erase(d[i].top()); d[i].push(dis(par[i],x)); if (d[i].size()) q[par[i]].push(d[i].top()); insert(q[par[i]]); } } } using namespace Query; int main() { bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1; scanf("%d",&n); for (int u,v,i=1;i<n;i++) { scanf("%d%d",&u,&v); link(u,v); } dfs(1); Init(); scanf("%d",&Q); char ch[5]; for (int cnt=n,x,i=1;i<=Q;i++) { scanf("%s",ch); if (ch[0]=='G') { if (cnt<=1) printf("%d ",cnt-1); else printf("%d ",ans.top()); } else { scanf("%d",&x); if (!light[x]) On(x),light[x]=!light[x],cnt--; else Off(x),light[x]=!light[x],cnt++; } } return 0; }