前置技能:点分治,动态点分治。
Part 0
首先,对于确定了路径上的一个点(也就是枚举重心的过程),那么如果这一条路径是一条收尾都是白色节点的路径,那么它由两条由重心出发的重点为白色节点的链拼接而成。故对于每个重心都要存每个以重心的一个端点,另一个端点为白色端点的链的信息,这个重心的答案(这个重心所管辖的区域以这个经过这个重心的合法路径的长度最大值就是两条最长的这样的链的长度之和)这个可以用堆来存,每次更新就是对于覆盖这个点的每个重心的堆的信息的更新,再开一个堆存所有重心的答案。(注意这个地方用set很容易TLE)。
Part 1
对于点分治,我们要避免的取出的两条链在重心的同一个儿子的字数内,然而容斥又很难解决这个问题,我们不妨对重心的每一个儿子开一个堆,存一个端点为重心,另一个端点为白色端点的且这个白色端点在这个重心的儿子的子树的信息,那么每个重心的堆只用维护每个儿子的堆里最大值即可。
注意路径的一个端点就是重心的情况要特殊考虑。
Part 2
本题用的堆是要支持删除的,这有两种实现方式,一种是手写可删堆(存每个元素的位置在堆中的位置),第二种是用两个堆来实现(一个堆存所有的数,一个堆存要删除的数,每次只用考虑堆顶元素是否要被删除,也就是两个堆的堆顶元素是否相等,若想等就让两个堆都弹出堆顶元素再判断,直到两个堆的堆顶元素不同)
实现方式如下:
struct Heap{
priority_queue<int>Add,Del;
int top(){
while(!Del.empty()&&Add.top()==Del.top())Add.pop(),Del.pop();
return Add.top();
}
void insert(int x){Add.push(x);}
void erase(int x){Del.push(x);}
int se_top(){//注意这里面的细节
int res=top();
erase(res);
int ans=top();insert(res);
return ans;
}
int size(){return Add.size()-Del.size();}
}
Part 3
本题的可删堆建议调试后在使用,放在代码里调试有点难调。
TLE可换用c++14 clang 4.0提交试试。(反正我的要)
AC代码:
#include<cstdio>
#include<set>
#include<queue>
#define M 100005
using namespace std;
void check_max(int &x,int y){if(x<y)x=y;}
void check_min(int &x,int y){if(x>y)x=y;}
bool val[M];
struct E{
int to,nx,d;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b,int d){
edge[++tot].to=b;
edge[tot].nx=head[a];
edge[tot].d=d;
head[a]=tot;
}
bool mark[M];
int sz[M],mx_sz[M],tot_sz,Root;
void Get_Root(int now,int fa){//求重心
sz[now]=1,mx_sz[now]=0;
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==fa||mark[nxt])continue;
Get_Root(nxt,now);
sz[now]+=sz[nxt];
check_max(mx_sz[now],sz[nxt]);
}
check_max(mx_sz[now],tot_sz-sz[now]);
if(mx_sz[now]<mx_sz[Root]||!Root)Root=now;
}
struct Heap{//可删堆
priority_queue<int>Add,Del;
int top(){
while(!Del.empty()&&Add.top()==Del.top())Add.pop(),Del.pop();
return Add.top();
}
void insert(int x){Add.push(x);}
void erase(int x){Del.push(x);}
int se_top(){//注意这里面的细节
int res=top();
erase(res);
int ans=top();insert(res);
return ans;
}
int size(){return Add.size()-Del.size();}
}ans,S[M*3],P[M];//P存以每个点为重心的最长链(经过这个点)
struct data_Anc{
int id_set,Center,dis;
//Center 重心 dis到重心的信息 id_set 这个重心(或是重心的儿子)的信息存在哪个堆里
}Anc[M][55];//存每个点被哪些重心所管辖
int sz_Anc[M],p_hp;//p_hp 堆的编号
void Get_dis(int now,int fa,int dis,int Center){
Anc[now][++sz_Anc[now]]=(data_Anc){p_hp,Center,dis};//添加信息
S[p_hp].insert(dis);
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==fa||mark[nxt])continue;
Get_dis(nxt,now,dis+edge[i].d,Center);
}
}
void Change(int now){
for(int i=1;i<=sz_Anc[now];i++){
data_Anc X=Anc[now][i];
int fa=X.Center,now_set=X.id_set;
if(P[fa].size()>1)ans.erase(P[fa].top()+P[fa].se_top());//先删去
if(S[now_set].size()>0)P[fa].erase(S[now_set].top());
if(val[now])S[now_set].erase(X.dis);//更新操作
else S[now_set].insert(X.dis);
if(S[now_set].size()>0)P[fa].insert(S[now_set].top());
if(P[fa].size()>1)ans.insert(P[fa].top()+P[fa].se_top());
}
val[now]^=1;
}
void Devide(int now){
mark[now]=1;
++p_hp;
Anc[now][++sz_Anc[now]]=(data_Anc){p_hp,now,0};//注意要把链的情况特殊考虑
S[p_hp].insert(0);P[now].insert(0);
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(mark[nxt])continue;
p_hp++;
Get_dis(nxt,now,edge[i].d,now);
P[now].insert(S[p_hp].top());
tot_sz=sz[nxt],Root=0;
Get_Root(nxt,now);
Devide(Root);
}
if(P[now].size()>1)ans.insert(P[now].top()+P[now].se_top());
}
char Q[10];
int main(){
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;i++)val[i]=1;//初始化
for(int i=1;i<n;i++){
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
Addedge(a,b,d);
Addedge(b,a,d);
}
tot_sz=n,Root=0;
Get_Root(1,0);
Devide(Root);
scanf("%d",&m);
int cnt=n;
while(m--){
scanf("%s",Q);
if(Q[0]=='A'){
if(ans.size()==0){
if(cnt==1)puts("0");
else puts("They have disappeared.");
}
else printf("%d\n",max(ans.top(),0));//两点重合的情况也要判
}else {
int x;
scanf("%d",&x);
if(val[x])cnt--;
else cnt++;
Change(x);
}
}
return 0;
}