首先,我们求出树的重链,然后对于每一条链,建一颗线段树
树大概长这样:
(其中用红边连起来的是一条条重链)
在线段树上,我们维护:
Opt(u):经过 u节点代表的链的其中一段 的两个白点间的最长路径长度
MaxL(u):u节点代表的链的左端点到最远的白点的距离
MaxR(u):u节点代表的链的右端点到最远的白点的距离
怎么维护呢?
我们再定义一些辅助变量方便描述:
D(i):节点i到最远的白点的距离
D2(i):节点i到次远的白点的距离
Dist(x,y):节点x,y之间的距离
Lc:线段树上左儿子
Rc:线段树上右儿子
当 l==r 即链只有一个节点时:
若u为黑色:
MaxL(u)=MaxR(u)=D(L)
Opt(u)=D(L)+D2(L)
若u为白色:
MaxL(u)=MaxR(u)=Max{D(L),0}
Opt(u)=Max{D(L)+D2(L),D(L)}
然后考虑如何push_up:
MaxL(u)=Max{MaxL(Lc),Dist(L,mid+1)+MaxL(Rc)}
MaxR(u)=Max{MaxR(Rc),Dist(mid,R)+MaxR(Lc)}
Opt(u)=Max{Opt(Lc),Opt(Rc),MaxR(Lc)+MaxL(Rc)+Dist(mid,mid+1)}
这样我们就维护好线段树啦,接下来考虑怎么在树上修改一个点的颜色:
假设我们要修改黄色点的颜色,那么我们肯定要修改被黄色笔框住的这条链
然后蓝色点肯定也被影响了,所以我们接下来修改被蓝色笔框住的这条链
再接下来绿色点也被影响力,所以我们修改被绿色笔框住的这条链
而下面已经没有点被影响了,修改结束
可见要修改一个点,我们只需要一层层链跳上去直到当前的链头无父节点为止
然后,我们还要考虑一下如何维护D(i)和D2(i) (节点到最远和次远白点的距离):
对此,我们对每个点维护一个大根堆,记录这个点到每个白点的距离
D(i)=s.top();
s.pop();
D2(i)=s.top();
s.push(D(i));
这样就可以求出D(i)和D2(i)啦
最后,在全局用个堆维护每条链的Opt,就可以直接查询了
这是Query on a tree IV的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100005;
const int inf=1e9;
struct work{
priority_queue <int> f,g;
inline void ins(int v) {
if(v != -inf) f.push(v); }
inline void era(int v) {
if(v != -inf) g.push(v); }
inline int top() {
while(1){
if(f.empty()) return -inf;
if(g.empty()) return f.top();
if(f.top() == g.top()) f.pop(), g.pop();
else return f.top();
}
}
}h[maxn], ans;
struct Edge{
int u,v,w,next;
}edge[maxn<<1];
struct seg{
int l,r,v,ls,rs;
}st[maxn<<2];
int wn,n,m,head[maxn],cnt,col[maxn],rt[maxn],onp;
int sz[maxn],fa[maxn],dep[maxn],son[maxn];
int tid[maxn],ord[maxn],ind,top[maxn],len[maxn];
inline int read() {
int p=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0') {
if(ch=='-') w = -1; ch=getchar();}
while(ch>='0'&&ch<='9') p=p*10+ch-'0',ch=getchar();
return p*w;
}
void add(int u,int v,int w){
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
#define dis(x) dep[ord[x]]
void push(int u,int l,int r){
int ls=st[u].ls,rs=st[u].rs,mid=(l+r)>>1;
st[u].l=max(st[ls].l,st[rs].l+dis(mid+1)-dis(l));//MaxL(u)=Max{MaxL(Lc),Dist(L,mid+1)+MaxL(Rc)}
st[u].r=max(st[rs].r,st[ls].r+dis(r)-dis(mid));//MaxR(u)=Max{MaxR(Rc),Dist(mid,R)+MaxR(Lc)}
st[u].v=max(max(st[ls].v,st[rs].v),st[ls].r+st[rs].l+dis(mid+1)-dis(mid));
//Opt(u)=Max{Opt(Lc),Opt(Rc),MaxR(Lc)+MaxL(Rc)+Dist(mid,mid+1)}
}
void build(int &u,int l,int r){
if(!u) u=++onp;
if(l==r){
int x=ord[l];
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa[x]||v==son[x]) continue;
h[x].ins(st[rt[v]].l+dep[v]-dep[x]);
}
int d1=h[x].top();
h[x].era(d1);
int d2=h[x].top();
h[x].ins(d1);
st[u].l=st[u].r=max(d1,0);
st[u].v=max(0,max(d1,d1+d2));//Opt(u)=Max{D(L)+D2(L),D(L)} 白色
return;
}
int mid =(l+r)>>1;
build(st[u].ls,l,mid);
build(st[u].rs,mid+1,r);
push(u,l,r);
}
void update(int u,int l,int r,int v,int s){
if(l==r){
if(v==s){
int d1=h[v].top();
h[v].era(d1);
int d2 = h[v].top();
h[v].ins(d1);
if(col[v]) st[u].l=st[u].r=d1,st[u].v=d1+d2;//Opt=D(L)+D2(L) 黑色
else st[u].l=st[u].r=max(d1,0),st[u].v=max(0,max(d1,d1+d2));//Opt(u)=Max{D(L)+D2(L),D(L)} 白色
}
else{
h[v].ins(st[rt[s]].l+dep[s]-dep[v]);
int d1 = h[v].top();
h[v].era(d1);
int d2 = h[v].top();
h[v].ins(d1);
if(col[v]) st[u].l=st[u].r=d1,st[u].v=d1+d2;//Opt=D(L)+D2(L) 黑色
else st[u].l=st[u].r=max(d1,0),st[u].v=max(0,max(d1,d1+d2));//Opt(u)=Max{D(L)+D2(L),D(L)} 白色
}
return;
}
int mid=(l+r)>>1;
if(tid[v]<=mid) update(st[u].ls,l,mid,v,s);
else update(st[u].rs,mid+1,r,v,s);
push(u,l,r);
}
void dfs1(int u){
sz[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
int w=edge[i].w;
if(v==fa[u]) continue;
fa[v]=u;
dep[v]=dep[u]+w;
dfs1(v);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp){
tid[u]=++ind;
top[u]=tp;
ord[ind]=u;
len[tp]++;
if(!son[u]) return;
dfs2(son[u],tp);
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int main(){
ios::sync_with_stdio(false);
memset(head,-1,sizeof(head));
wn=n=read();
int u,v,w;
for(int i=1;i<n;i++){
u=read();v=read();w=read();
add(u,v,w);
add(v,u,w);
}
dfs1(1);
dfs2(1,1);
ans.ins(0);
for(int i=n;i;i--){
int u=ord[i];
if(u!=top[u]) continue;
build(rt[u],tid[u],tid[u]+len[u]-1); //每一条链建一线段树
ans.ins(st[rt[u]].v);
}
m=read();
char ch;
for(int i=1;i<=m;i++){
ch=getchar();
while(ch!='C'&&ch!='A') ch=getchar();
if(ch=='C'){
int x=read();
col[x]^= 1;
if(col[x]==0) wn++;
else wn --;
for(int u=x,p=u;u;u=fa[u]){
int tp=top[u];
int p1=st[rt[tp]].v,d1=st[rt[tp]].l;
if(fa[tp]) h[fa[tp]].era(st[rt[tp]].l+dep[tp]-dep[fa[tp]]);
update(rt[tp],tid[tp],tid[tp]+len[tp]-1,u,p);
int p2=st[rt[tp]].v,d2=st[rt[tp]].l;
if(p1!=p2) ans.era(p1),ans.ins(p2);
p=u=tp;
}
}
else {
if(wn==0) printf("They have disappeared.\n");
else printf("%d\n", ans.top());
}
}
return 0;
}
捉迷藏的代码只需要改一下输入输出,就不贴了