BZOJ 3531 旅行 (树链剖分+线段树)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3531

题意:给出一棵树,每个节点有两个属性(x,y)。(1)改变某个节点的x属性或y属性;(2)从s到t,输出中间节点与s节点y属性相同的所有节点的x属性的和或最大值。

思路:树链剖分。之后是线段树维护。难操作的是这里有个y属性。T[y]表示y属性的根,有多少种y属性有多少个线段树。

struct node
{
    i64 sum,Max;
    int L,R;
};


int top[N],pos[N],b[N],cnt;


int W[N],C[N];

node a[N*40];
int e;
int T[N];

int n;

void pushUp(int t)
{
    a[t].sum=a[a[t].L].sum+a[a[t].R].sum;
    a[t].Max=max(a[a[t].L].Max,a[a[t].R].Max);
}


void build(int L,int R,int pos,int val,int &t)
{
    if(!t) t=++e,a[t].L=a[t].R=0;
    if(L==R)
    {
        a[t].sum=a[t].Max=val;
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid) build(L,mid,pos,val,a[t].L);
    else build(mid+1,R,pos,val,a[t].R);
    pushUp(t);
}



void changeType(int x,int c)
{
    if(c==C[x]) return;
    int p=pos[x];

    build(1,n,p,0,T[C[x]]);
    C[x]=c;
    build(1,n,p,W[x],T[C[x]]);
}

void changeVal(int x,int w)
{
    if(w==W[x]) return;
    int p=pos[x];
    W[x]=w;
    build(1,n,p,W[x],T[C[x]]);
}


i64 querySum(int L,int R,int ll,int rr,int t)
{
    if(!t) return 0;
    if(ll==L&&rr==R) return a[t].sum;
    int mid=(L+R)>>1;
    if(rr<=mid) return querySum(L,mid,ll,rr,a[t].L);
    if(ll>mid) return querySum(mid+1,R,ll,rr,a[t].R);
    return querySum(L,mid,ll,mid,a[t].L)+querySum(mid+1,R,mid+1,rr,a[t].R);
}



i64 queryMax(int L,int R,int ll,int rr,int t)   
{
    if(!t) return 0;

    if(ll==L&&rr==R) return a[t].Max;
    int mid=(L+R)>>1;
    if(rr<=mid) return queryMax(L,mid,ll,rr,a[t].L);
    if(ll>mid) return queryMax(mid+1,R,ll,rr,a[t].R);
    i64 x=queryMax(L,mid,ll,mid,a[t].L);
    i64 y=queryMax(mid+1,R,mid+1,rr,a[t].R);
    return max(x,y);
}



vector<int> g[N];
int size[N];
int m;



int isOnSameChain(int x,int y)
{
    return top[x]==top[y];
}

int getTop(int x)
{
    return top[x];
}

int dep[N];
int f[N];

void calSum(int x,int y)
{
    i64 ans=0;
    int type=C[x];
    while(!isOnSameChain(x,y))
    {
        int top1=getTop(x);
        int top2=getTop(y);
        if(dep[top1]<dep[top2]) swap(top1,top2),swap(x,y);
        ans+=querySum(1,n,pos[top1],pos[x],T[type]);
        x=f[top1];
    }
    if(pos[x]>pos[y]) swap(x,y);
    ans+=querySum(1,n,pos[x],pos[y],T[type]);
    printf("%lld\n",ans);
}

void calMax(int x,int y)
{
    i64 ans=0;
    int type=C[x];
    while(!isOnSameChain(x,y))
    {
        int top1=getTop(x);
        int top2=getTop(y);
        if(dep[top1]<dep[top2]) swap(top1,top2),swap(x,y);
        i64 tmp=queryMax(1,n,pos[top1],pos[x],T[type]);
        upMax(ans,tmp);
        x=f[top1];
    }
    if(pos[x]>pos[y]) swap(x,y);
    i64 tmp=queryMax(1,n,pos[x],pos[y],T[type]);
    upMax(ans,tmp);
    printf("%lld\n",ans);
}

int son[N];


void DFS(int u,int pre)
{
    f[u]=pre;
    dep[u]=dep[pre]+1;
    size[u]=1;
    son[u]=0;
    int i,v,Max=0,t=0;
    FOR0(i,SZ(g[u]))
    {
        v=g[u][i];
        if(v==pre) continue;
        DFS(v,u);
        size[u]+=size[v];
        if(Max<size[v]) Max=size[v],t=v;
    }
    son[u]=t;
}


void DFS1(int u,int pre,int root)
{
    top[u]=root;
    cnt++;
    pos[u]=cnt;
    b[cnt]=u;

    if(son[u])
    {
        DFS1(son[u],u,root);
        int i,v;
        FOR0(i,SZ(g[u]))
        {
            v=g[u][i];
            if(v==pre||v==son[u]) continue;
            DFS1(v,u,v);
        }
    }
}


int main()
{
    RD(n,m);
    int i,x,y,z;
    FOR1(i,n) RD(W[i],C[i]);
    FOR1(i,n-1)
    {
        RD(x,y);
        g[x].pb(y);
        g[y].pb(x);
    }


    DFS(1,0);


    DFS1(1,0,1);


    for(i=1;i<=n;i++) build(1,n,pos[i],W[i],T[C[i]]);

    char op[10];
    while(m--)
    {
        RD(op); RD(x,y);
        if(op[0]=='C'&&op[1]=='C')
        {
            changeType(x,y);
        }
        else if(op[0]=='C'&&op[1]=='W')
        {
            changeVal(x,y);
        }
        else if(op[0]=='Q'&&op[1]=='S')
        {
            calSum(x,y);
        }
        else
        {
            calMax(x,y);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值