题目大意:
给定一个节点初始颜色全为白色的点,要求支持两种操作:
1.将节点x颜色翻转;
2.询问树中相距最远的两个白点的距离。
1≤n,m≤100000
解题思路:
为了叙述方便,其他的一些定义:ls左儿子,rs有儿子
首先把边权附到儿子结点上,记为len(len会引发很多细节问题,一定要小心)
实边上的信息维护一个lmax,rmax,maxs,sum。
lmax这个子splay表示的一段重链最浅的白点出发的最长链长,rmax从该实链最深的白点出发的最长链长(但不能越过fa[x]。这样lmax[rs],rmax[ls]就是实树中连接到x的链了),maxs即答案,包括实边虚边的最远两白点距离 ,sum表示该子splay段实链总长。
虚边就类似点分治时的想法,记录只到x点的链,和x子树中已经构成路径的两个白点(只管虚子树),用multiset维护。
先讨论x的虚子树对maxs[x]的影响,实边要复杂一些
最长和次长到x的链可以合并成一条路径
子树里已经形成的最大路径也可以更新
如果x是白点,那么maxs[x]就可以用最长链更新了
虚子树信息的维护主要是在access中的虚实切换
对于要从实变虚的原右儿子,maxs[rs]扔进路径的set里,lmax[rs]扔进链的set里,因为要连接到x点,所以是lmax
同样,从虚变实的新右儿子,把maxs[y]从set里删去,把lmax[y]从set里删去
虚部就完成了。
对于实链我们可以发现一个性质,LCT中splay的任意一个子树,对应的都是原树中实链连续的一段。
先更新lmax和rmax
lmax要过整棵子splay的最浅点
那么有lmax[x]=max(lmax[ls](不过x),max(虚链中最长+整个左儿子代表的一段,lmax[rs]+整个左儿子代表的一段))。
rmax同理,注意x对应的这条边,会有一些细节问题。
于是maxs就又有了两种新的更新方式
maxs[x]=max(maxs[x],rmax[ls]+max(虚链,lmax[rs]))
maxs[x]=max(maxs[x],lmax[rs]+max(虚链,rmax[ls]))
完结撒花。
#include<bits/stdc++.h>
#define ls son[x][0]
#define rs son[x][1]
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005,INF=0x3f3f3f3f;
int n,m,ans=-INF;
int tot,first[N],to[N<<1],w[N<<1],nxt[N<<1],c[N];
int son[N][2],fa[N],sum[N],lmax[N],rmax[N],maxs[N],val[N],len[N];
multiset<int>chain[N],path[N];
int fir(multiset<int> &s){return s.size()?*--s.end():-INF;}
int sec(multiset<int> &s){return s.size()>1?*--(--s.end()):-INF;}
void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void update(int x)
{
sum[x]=sum[ls]+len[x]+sum[rs];
int cha=max(val[x],fir(chain[x]));
int L=max(cha,rmax[ls]+len[x]);
int R=max(cha,lmax[rs]);
lmax[x]=max(lmax[ls],sum[ls]+len[x]+R);
rmax[x]=max(rmax[rs],sum[rs]+L);
maxs[x]=max(rmax[ls]+len[x]+R,lmax[rs]+L);
maxs[x]=max(maxs[x],max(maxs[ls],maxs[rs]));
maxs[x]=max(maxs[x],fir(path[x]));
maxs[x]=max(maxs[x],fir(chain[x])+sec(chain[x]));
if(!val[x])maxs[x]=max(0,max(maxs[x],fir(chain[x])));
}
int which(int x){return son[fa[x]][1]==x;}
bool rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
void rotate(int x)
{
int y=fa[x],z=fa[y],t=which(x);
if(!rt(y))son[z][which(y)]=x;
fa[x]=z,fa[y]=x;
son[y][t]=son[x][t^1],son[x][t^1]=y;
if(son[y][t])fa[son[y][t]]=y;
update(y),update(x);
}
void splay(int x)
{
while(!rt(x))
{
if(!rt(fa[x]))
which(x)==which(fa[x])?rotate(fa[x]):rotate(x);
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
{
splay(x);
if(rs)chain[x].insert(lmax[rs]),path[x].insert(maxs[rs]);
if(y)fa[y]=x,chain[x].erase(chain[x].find(lmax[y])),path[x].erase(path[x].find(maxs[y]));
rs=y,update(x);
}
}
void modify(int x)
{
access(x),splay(x);
c[x]^=1,val[x]=(!c[x])?0:-INF;
update(x),ans=maxs[x];
}
void dfs(int u)
{
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa[u])continue;
fa[v]=u,len[v]=w[e];
dfs(v);
chain[u].insert(lmax[v]),path[u].insert(maxs[v]);
}
update(u);
}
int main()
{
//freopen("lx.in","r",stdin);
int x,y,z;
n=getint();
for(int i=1;i<n;i++)
{
x=getint(),y=getint(),z=getint();
add(x,y,z),add(y,x,z);
}
for(int i=0;i<=n;i++)lmax[i]=rmax[i]=maxs[i]=-INF;
dfs(1);ans=maxs[1];
m=getint();char s[5];
while(m--)
{
scanf("%s",s);
if(s[0]=='A')
{
if(ans<0)puts("They have disappeared.");
else printf("%d\n",ans);
}
else modify(getint());
}
return 0;
}