【BZOJ3531】旅行(线段树)

传送门

I Think

    题意:一棵树上节点有两个点权c1,c2,求(x,y)两点路径中c2与x的c2相同的城市的c1总和/最大值,两个点权均带修改。
    算法:线段树(动态开点)+树链剖分
    实现:对每一种c2(信仰)建立线段树,维护以之为信仰的城市的c1。
    Attention!
    1)动态删点要带&
    2)对于每一个修改操作,不仅在线段树上更改,还要更改原数组的值。

Code

#include<cstdio>
using namespace std;

const int sm = 1e5+2;
const int sn = sm<<6;

template <typename T> void read(T &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
char Hint[3];
int N,Q,tot,t;
int sum[sn],mx[sn],c[sm],w[sm],T[sm],stk[sm];
int hd[sm],to[sm<<1],nxt[sm<<1],Ls[sn],Rs[sn];
int Fa[sm][18],dep[sm],son[sm],sz[sm],top[sm],pl[sm];

void Swap(int &x,int &y) { int t=x;x=y;y=t; }
int Max(int x,int y) { return x>y?x:y; }
void Add(int u,int v) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot;
}
void Dfsa(int x,int fa) {
    Fa[x][0]=fa,dep[x]=dep[fa]+1,sz[x]=1;
    for(int i=1;i<=17;++i)
        if(Fa[x][i-1])Fa[x][i]=Fa[Fa[x][i-1]][i-1];
        else break;
    for(int i=hd[x];i;i=nxt[i])
        if(to[i]!=fa) {
            Dfsa(to[i],x),sz[x]+=sz[to[i]];
            if(sz[son[x]]<sz[to[i]])son[x]=to[i];
        }
}
void Dfsb(int x,int tp) {
    pl[x]=++tot,top[x]=tp;
    if(son[x]) Dfsb(son[x],top[x]);
    for(int i=hd[x];i;i=nxt[i])
        if(to[i]!=Fa[x][0]&&to[i]!=son[x])
            Dfsb(to[i],to[i]);
}
void Up(int rt) {
    mx[rt]=Max(mx[Ls[rt]],mx[Rs[rt]]);
    sum[rt]=sum[Ls[rt]]+sum[Rs[rt]];
}
void del(int &rt) {
    stk[++t]=rt,mx[rt]=sum[rt]=0,rt=0;
}
void Modify(int &rt,int l,int r,int p,int val) {
    if(!rt) { if(t) rt=stk[t--]; else rt=++tot;}
    if(l==r) { mx[rt]=sum[rt]=val; if(!mx[rt])del(rt); return; }
    int m=(l+r)>>1;
    if(p<=m) Modify(Ls[rt],l,m,p,val);
    else Modify(Rs[rt],m+1,r,p,val);
    if(!Ls[rt]&&!Rs[rt]) del(rt);
    else Up(rt);
}
int Maxn(int rt,int l,int r,int a,int b) {
    if(!rt||l>b||r<a) return 0;
    if(a<=l&&r<=b) return mx[rt];
    int m=(l+r)>>1,ans=0;
    if(a<=m) ans=Max(ans,Maxn(Ls[rt],l,m,a,b));
    if(b> m) ans=Max(ans,Maxn(Rs[rt],m+1,r,a,b));
    return ans;
}
int Sum(int rt,int l,int r,int a,int b) {
    if(!rt)return 0;
    if(a<=l&&r<=b) return sum[rt];
    int m=(l+r)>>1,ans=0;
    if(a<=m) ans+=Sum(Ls[rt],l,m,a,b);
    if(b> m) ans+=Sum(Rs[rt],m+1,r,a,b);
    return ans;
}
int Q_max(int a,int b) {
    int og=T[c[a]],tmp=0;
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]])Swap(a,b);
        tmp=Max(tmp,Maxn(og,1,N,pl[top[a]],pl[a]));
        a=Fa[top[a]][0];
    }
    if(pl[a]>pl[b]) Swap(a,b);
    tmp=Max(tmp,Maxn(og,1,N,pl[a],pl[b]));
    return tmp;
}
int Q_sum(int a,int b) {
    int og=T[c[a]],ret=0;
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) Swap(a,b);
        ret+=Sum(og,1,N,pl[top[a]],pl[a]);
        a=Fa[top[a]][0];
    }
    if(pl[a]>pl[b])Swap(a,b);
    ret+=Sum(og,1,N,pl[a],pl[b]);
    return ret; 
}
int Lca(int u,int v) {
    if(dep[u]!=dep[v]) {
        if(dep[u]<dep[v]) Swap(u,v);
        for(int i=17;i>=0;--i)
            if(dep[Fa[u][i]]>dep[v])
                u=Fa[u][i];
        u=Fa[u][0];
    }
    if(u==v) return v;
    for(int i=17;i>=0;--i)
        if(Fa[u][i]!=Fa[v][i])
            u=Fa[u][i],v=Fa[v][i];
    return Fa[u][0];
}
int main() {
    read(N),read(Q);
    for(int i=1;i<=N;++i)
        read(w[i]),read(c[i]);
    for(int i=1,a,b;i<N;++i)
        read(a),read(b),Add(a,b);
    tot=0,Dfsa(1,0),Dfsb(1,1),tot=0;
    for(int i=1;i<=N;++i)
        Modify(T[c[i]],1,N,pl[i],w[i]);
    for(int i=1,x,y,lc,S;i<=Q;++i) {
        scanf("%s",Hint);
        read(x),read(y);
        if(Hint[0]=='C') {
            if(Hint[1]=='C') {
                Modify(T[c[x]],1,N,pl[x],0),c[x]=y;
                Modify(T[c[x]],1,N,pl[x],w[x]);
            } 
            else Modify(T[c[x]],1,N,pl[x],y),w[x]=y;//别忘记更新
        }
        else{
            lc=Lca(x,y);
            if(Hint[1]=='S')
                printf("%d\n",Q_sum(x,lc)+Q_sum(y,lc)-(c[lc]==c[x])*w[lc]);
            else
                printf("%d\n",Max(Q_max(x,lc),Q_max(y,lc)));
        }
    } 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值