P4679 [ZJOI2011] 道馆之战

题意

给一棵 \(n\) 个点的树,每个点有一个两字符的字符串作为权值,其中每个字符为 .#\(m\) 次操作,单点修改,查询从 \(u\) 走到 \(v\),不能经过 #,每个 . 只能经过一次,最多能经过多少个 .
\(n\le5\times10^4,m\le10^5\)

思路

用树剖将树问题转化为序列问题,用线段树维护。每个点存储 \(a_{0/1,0/1}\) 表示当前区间第一步走 \(A/B\) 房间,最后一步走 \(A/B\) 房间的最大点数。\(in_{0/1}\) 表示当前区间从前向后走第一步走 \(A/B\) 房间的最大步数,\(out_{0/1}\) 表示到这走第一步走 \(A/B\) 房间的最大步数。查询链的时候把 \(u\rightarrow v\) 拆分成 \(u\rightarrow lca(u,v)\)\(lca(u,v)\rightarrow v\),要注意一下每条路径的方向。

代码

// Problem: P4679 [ZJOI2011] 道馆之战
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4679
// Memory Limit: 250 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=50010,inf=100000000;
int n,m;
vector<int>e[maxn];
struct node{
    int a[2][2],in[2],out[2];
    node(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=in[0]=in[1]=out[0]=out[1]=0;}
};
node merge(node a,node b){
    node ans;
    ans.a[0][0]=max(a.a[0][0]+b.a[0][0],a.a[0][1]+b.a[1][0]);
    ans.a[0][1]=max(a.a[0][0]+b.a[0][1],a.a[0][1]+b.a[1][1]);
    ans.a[1][0]=max(a.a[1][0]+b.a[0][0],a.a[1][1]+b.a[1][0]);
    ans.a[1][1]=max(a.a[1][0]+b.a[0][1],a.a[1][1]+b.a[1][1]);
    ans.in[0]=max({a.in[0],a.a[0][0]+b.in[0],a.a[0][1]+b.in[1],-inf});
    ans.in[1]=max({a.in[1],a.a[1][0]+b.in[0],a.a[1][1]+b.in[1],-inf});
    ans.out[0]=max({b.out[0],a.out[0]+b.a[0][0],a.out[1]+b.a[1][0],-inf});
    ans.out[1]=max({b.out[1],a.out[0]+b.a[0][1],a.out[1]+b.a[1][1],-inf});
    ans.a[0][0]=max(ans.a[0][0],-inf);
    ans.a[0][1]=max(ans.a[0][1],-inf);
    ans.a[1][0]=max(ans.a[1][0],-inf);
    ans.a[1][1]=max(ans.a[1][1],-inf);
    return ans;
}
void swap(node&a){swap(a.a[0][1],a.a[1][0]),swap(a.in[0],a.out[0]),swap(a.in[1],a.out[1]);}
class Segment_Tree{
private:
    node t[maxn*4];
    void push_up(int u){t[u]=merge(t[u<<1],t[u<<1|1]);}
    void update(int u,int l,int r,int d,string z){
        if(l>d||r<d) return ;
        if(l==r){
            t[u].a[0][0]=t[u].a[0][1]=t[u].a[1][0]=t[u].a[1][1]=-inf;
            if(z[0]=='.') t[u].a[0][0]=1;
            if(z[1]=='.') t[u].a[1][1]=1;
            if(z[0]=='.'&&z[1]=='.') t[u].a[0][1]=t[u].a[1][0]=2;
            t[u].in[0]=max(t[u].a[0][0],t[u].a[0][1]);
            t[u].in[1]=max(t[u].a[1][0],t[u].a[1][1]);
            t[u].out[0]=max(t[u].a[0][0],t[u].a[1][0]);
            t[u].out[1]=max(t[u].a[0][1],t[u].a[1][1]);
            return ;
        }
        int mid=l+r>>1;
        update(u<<1,l,mid,d,z),update(u<<1|1,mid+1,r,d,z);
        t[u]=merge(t[u<<1],t[u<<1|1]);
    }
    node query(int u,int l,int r,int ll,int rr){
        if(ll<=l&&r<=rr) return t[u];
        if(l>rr||r<ll) return node();
        int mid=l+r>>1;
        if(rr<=mid) return query(u<<1,l,mid,ll,rr);
        if(ll>mid) return query(u<<1|1,mid+1,r,ll,rr);
        return merge(query(u<<1,l,mid,ll,rr),query(u<<1|1,mid+1,r,ll,rr));
    }
public:
    void update(int d,string z){update(1,1,n,d,z);}
    node query(int l,int r){return query(1,1,n,l,r);}
}t;
namespace HLD{
    int dfn[maxn],cnt_dfn,son[maxn],sz[maxn],fa[maxn],deep[maxn],top[maxn];
    void dfs(int u){
        sz[u]=1;
        deep[u]=deep[fa[u]]+1;
        for(int v:e[u]){
            if(v==fa[u]) continue;
            fa[v]=u;
            dfs(v);
            sz[u]+=sz[v];
            if(sz[v]>sz[son[u]]) son[u]=v;
        }
    }
    void dfs2(int u,int nwtop){
        top[u]=nwtop;
        dfn[u]=++cnt_dfn;
        if(son[u]) dfs2(son[u],nwtop);
        for(int v:e[u]) if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
    }
    void build(){
        dfs(1);
        dfs2(1,1);
    }
    void update(int u,string s){t.update(dfn[u],s);}
    int query(int u,int v){
        node ansu,ansv;
        ansu.a[0][0]=ansv.a[0][0]=-1;
        while(top[u]!=top[v]){
            if(deep[top[u]]<deep[top[v]]){
                node nw=t.query(dfn[top[v]],dfn[v]);
                if(~ansv.a[0][0]) ansv=merge(nw,ansv);
                else ansv=nw;
                v=fa[top[v]];
            }
            else{
                node nw=t.query(dfn[top[u]],dfn[u]);
                swap(nw);
                if(~ansu.a[0][0]) ansu=merge(ansu,nw);
                else ansu=nw;
                u=fa[top[u]];
            }
        }
        if(deep[u]<deep[v]){
            node nw=t.query(dfn[u],dfn[v]);
            if(~ansv.a[0][0]) ansv=merge(nw,ansv);
             else ansv=nw;
        }
        else{
            node nw=t.query(dfn[v],dfn[u]);
            swap(nw);
            if(~ansu.a[0][0]) ansu=merge(ansu,nw);
            else ansu=nw;
        }
        node ans;
        if(ansu.a[0][0]==-1) ans=ansv;
        else if(ansv.a[0][0]==-1) ans=ansu;
        else ans=merge(ansu,ansv);
        return max({ans.in[0],ans.in[1],0});
    }
};
signed main(){
    read(n,m);
    for(int i=1;i<n;i++){
        int u,v;read(u,v);
        e[u].push_back(v),e[v].push_back(u);
    }
    HLD::build();
    for(int i=1;i<=n;i++){
        string s;read(s);
        HLD::update(i,s);
    }
    for(int i=1;i<=m;i++){
        char op;int u,v;string s;
        read(op,u);
        if(op=='C') read(s),HLD::update(u,s);
        else read(v),write(HLD::query(u,v)),write("\n");
    }
    return 0;
}
posted @ 2026-05-22 22:28  Link-Cut_Trees  阅读(15)  评论(0)    收藏  举报