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);

        }

    }

}

 

你可能感兴趣的:(线段树)