此举是为了重新回到CSDN,自己博客写炸了。
然后嗯,先是推广一下我们的oj
小白菜oj
oj很好,很适合初学者
今次的题目还是很难得。
传送门
具体看看代码吧,重点是ST表管理和普通树管理。
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int bin[20],Log[200005];//bin[i]表示2的i次方,Log就是log
int n,m,G,cnt,dfn,sum,tott;
int tot[100005],f[100005],dep[100005];// f[i]数组表示以i为根之后剩下所有子树家族最大的子树的家族数目
//dep表示到一号节点的距离
int mn[18][200005],ys[100005],fa[100005];//mn[i][j]表示dfs序中从j到j+2^i-1的最小深度的点
bool v[100005],col[100005];//col表示当前这个点的颜色,1为黑色,0为白色
struct node{
int x,y,c,next;
} a[200005];
int len,last[100005];
void ins(int x,int y,int c){
len++;
a[len].x=x;a[len].y=y;a[len].c=c;
a[len].next=last[x];last[x]=len;
}
/*
做法。
对于每个节点我们维护两个堆(堆的堆顶最大)
每个节点的第一个堆维护所有子树内的节点到自己父亲节点的距离
第二个堆维护所有子节点第一个堆的堆顶(最大值)
那么相对于每一个节点来说,第二个堆的最大值加次大值就是子树内经过这个节点的最长链!!
全局维护一个堆,记录所有节点第二个堆的最大值和次大值的和。堆顶(最大值)就是答案
*/
struct heap{
priority_queue<int> A,B;
void push(int x){
A.push(x);
}
void erase(int x){
B.push(x);
}
void pop(){
while(B.size()&&A.top()==B.top())
A.pop(),B.pop();
A.pop();
}
int top(){
while(B.size()&&A.top()==B.top())
A.pop(),B.pop();
if(!A.size())return 0;
return A.top();
}
int size(){
return A.size()-B.size();
}
int stop(){
if(size()<2)return 0;
int x=top();pop();
int y=top();push(x);
return y;
}
} A,B[110000],C[110000];
void dfs(int x,int fa){
mn[0][++dfn]=dep[x];
ys[x]=dfn;
for(int k=last[x]; k; k=a[k].next){
int y=a[k].y;
if(y!=fa){
dep[y]=dep[x]+a[k].c;
dfs(y,x);
mn[0][++dfn]=dep[x];//回溯,很重要,不加回溯会错,先思考,视频里会详细讲解。
}
}
}
/*
getrt
找到树的重心并存在G里。
树的重心是一个点a
删掉树的重心之后所剩子树家族最大的最小(好好理解这句话!!!)
*/
void getrt(int x,int fa){
tot[x]=1;f[x]=0;
for(int k=last[x]; k; k=a[k].next){
int y=a[k].y;
if(y!=fa&&v[y]==false){
getrt(y,x);
tot[x]+=tot[y];
f[x]=max(f[x],tot[y]);
}
}
f[x]=max(f[x],sum-tot[x]);
if(f[x]<f[G])
G=x;
}
//按照新的树去建父子关系 ,当前这一层的中心去连接下一层的重心
void divi(int x){
v[x]=true;
for(int k=last[x]; k; k=a[k].next){
int y=a[k].y;
if(v[y]==false){
sum=tot[y];G=0;
getrt(y,x);
fa[G]=x;divi(G);
}
}
}
//求x到y路径上深度最小的点的深度(最近公共祖先的深度 )
int rmq(int x,int y){
x=ys[x];y=ys[y];
if(x>y)
swap(x,y);
int t=Log[y-x+1];
return min(mn[t][x],mn[t][y-bin[t]+1]);
}
//求x到y之间的距离
int dis(int x,int y){
return dep[x]+dep[y]-2*rmq(x,y);
}
//把v节点变成黑色,x对于f有影响
void turn_black(int f,int x){
if(f==x){
if(B[f].size()==1)
A.push(B[f].top());
}
if(!fa[f])
return;
int ff=fa[f],D=dis(ff,x),tmp=C[f].top();
C[f].push(D);
if(D>tmp){
int mx=B[ff].top()+B[ff].stop(),size=B[ff].size();
if(tmp)
B[ff].erase(tmp);
B[ff].push(D);
int now=B[ff].top()+B[ff].stop();
if(now>mx)
{
if(size>=2) A.erase(mx);
if(B[ff].size()>=2) A.push(now);
}
}
turn_black(ff,x);
}
//把v节点变成白色,v对于u有影响
void turn_white(int f,int x){
if(f==x){
if(B[f].size()==1)
A.erase(B[f].top());
}
if(!fa[f])
return;
int ff=fa[f],D=dis(ff,x),tmp=C[f].top();
C[f].erase(D);
if(D==tmp){
int mx=B[ff].top()+B[ff].stop(),size=B[ff].size();
B[ff].erase(D);
if(C[f].top())
B[ff].push(C[f].top());
int now=B[ff].top()+B[ff].stop();
if(now<mx){
if(size>=2)
A.erase(mx);
if(B[ff].size()>=2)
A.push(now);
}
}
turn_white(ff,x);
}
int main(){
bin[0]=1;
for(int i=1;i<20;i++)
bin[i]=bin[i-1]*2;
Log[0]=-1;
for(int i=1;i<=200000;i++)
Log[i]=Log[i/2]+1;
scanf("%d",&n);
len=0;memset(last,0,sizeof(last));
for(int i=1;i<n;i++){
int x,y,c;scanf("%d%d%d",&x,&y,&c);
ins(x,y,c);ins(y,x,c);
}
dfs(1,0);
// mn[i][j]表示j到j+2^i-1最小深度的点
// ST表
for(int i=1;i<=Log[dfn];i++)
for(int j=1;j<=dfn;j++)
if(j+bin[i]-1<=dfn)
mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]);
//j到j+2^i-1的最小值等于min(前半部分最小值,后半部分最小值)
G=0;f[0]=2147483647;
sum=n;getrt(1,0);
fa[G]=0;divi(G);
for(int i=1;i<=n;i++)
col[i]=1; //col等于1代表节点为黑色,等于0为白色
for(int i=1;i<=n;i++){
turn_black(i,i);
tott++;//tott表示当前黑色节点的总数
}
char ch[2];
scanf("%d",&m);
while(m--){
scanf("%s",ch+1);
if(ch[1]=='A'){
if(tott<=0)
printf("They have disappeared.\n");
else
printf("%d\n",A.top());//全局的堆顶即为答案
}
else{
int x;
scanf("%d",&x);
if(col[x])
turn_white(x,x),tott--;
else
turn_black(x,x),tott++;
col[x]^=1;//变颜色
}
}
return 0;
}