[BZOJ1095][ZJOI2007]捉迷藏 & Query on a tree IV(树链剖分)

首先,我们求出树的重链,然后对于每一条链,建一颗线段树
树大概长这样:

(其中用红边连起来的是一条条重链)
在线段树上,我们维护
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;
}

捉迷藏的代码只需要改一下输入输出,就不贴了

猜你喜欢

转载自blog.csdn.net/Emma2oo6/article/details/103432013