bzoj3531

  树链剖分题,对于每一种信仰我们建一棵线段树,因为是动态开点,所以不会mle,然后树链剖分后维护区间最大和区间和就好了,注意单点修改(权值,信仰)完成之后应该也将w[i],c[i]更新,因为下一次的询问可能会用到它

 

/**************************************************************

    Problem: 3531

    User: syh0313

    Language: C++

    Result: Accepted

    Time:9804 ms

    Memory:107564 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cmath>

#include <cstring>

#include <string>

#define lch a[n].lc

#define rch a[n].rc

using namespace std;

const int inf=1000000000;

const int maxe=200010;

const int maxd=100010;

int n,q,topt,to[maxe],nt[maxe],st[maxe],c[maxd],w[maxd],root[maxd];

int dep[maxd],fa[maxd],rem[maxd],size[maxd],top[maxd];

int dfn[maxd],line[maxd],dfn_num,cnt;

bool f[maxd];

char s[5];

struct tree

{

    int l,r,lc,rc,ma,sum;

}a[4000010];

void add(int x,int y)

{to[++topt]=y; nt[topt]=st[x]; st[x]=topt;}

void dfs1(int x,int d)

{

    f[x]=1; dep[x]=d; size[x]=1;

    int p=st[x],ma=0;

    while (p)

    {

        if (!f[to[p]])

        {

            fa[to[p]]=x;

            dfs1(to[p],d+1);

            size[x]+=size[to[p]];

            if (ma<size[to[p]]) {ma=size[to[p]]; rem[x]=to[p];}

        }

        p=nt[p];

    }

}

void dfs2(int x)

{

    if (rem[fa[x]]==x) top[x]=top[fa[x]];else top[x]=x;

    f[x]=1; dfn[x]=++dfn_num; line[dfn_num]=x;

    if (rem[x]) dfs2(rem[x]);

    int p=st[x];

    while (p)

    {

        if (!f[to[p]]) dfs2(to[p]);

        p=nt[p];

    }

}

void updata(int n)

{

    a[n].ma=max(a[lch].ma,a[rch].ma);

    a[n].sum=a[lch].sum+a[rch].sum;

}

void build_tree(int &n,int l,int r,int lc,int k)

{

    if (!n) n=++cnt;

    if (l==r && l==lc) {a[n].ma=k; a[n].sum=k; return;}

    if (l==r) return ;

    int mid=(l+r)>>1;

    if (lc<=mid) build_tree(lch,l,mid,lc,k);

    else build_tree(rch,mid+1,r,lc,k);

    updata(n);

}

int qurymax(int n,int L,int R,int l,int r)

{

    if (L==l && R==r) return a[n].ma;

    int mid=(L+R)>>1;

    if (r<=mid) return qurymax(lch,L,mid,l,r);

    else if (l>=mid+1) return qurymax(rch,mid+1,R,l,r);

    else return max(qurymax(lch,L,mid,l,mid),qurymax(rch,mid+1,R,mid+1,r));

}

int qmax(int x,int y,int col)

{

    int ans=-inf;

    while (top[x]!=top[y])

    {

        if (dep[top[x]]<dep[top[y]]) swap(x,y);

        ans=max(ans,qurymax(root[col],1,n,dfn[top[x]],dfn[x]));

        x=fa[top[x]];

    }

    int l=min(dfn[x],dfn[y]),r=max(dfn[x],dfn[y]);

    ans=max(ans,qurymax(root[col],1,n,l,r));

return ans;

}

int qurysum(int n,int L,int R,int l,int r)

{

    if (L==l && R==r) return a[n].sum;

    int mid=(L+R)>>1;

    if (r<=mid) return qurysum(lch,L,mid,l,r);

    else if (l>=mid+1) return qurysum(rch,mid+1,R,l,r);

    else return qurysum(lch,L,mid,l,mid)+qurysum(rch,mid+1,R,mid+1,r);

}

int qsum(int x,int y,int col)

{

    int sum=0;

    while (top[x]!=top[y])

    {

        if (dep[top[x]]<dep[top[y]]) swap(x,y);

        sum+=qurysum(root[col],1,n,dfn[top[x]],dfn[x]);

        x=fa[top[x]];

    }

    int l=min(dfn[x],dfn[y]),r=max(dfn[x],dfn[y]);

    sum+=qurysum(root[col],1,n,l,r);

return sum;

}

int main()

{

    scanf("%d%d",&n,&q);

    for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);

    for (int i=1;i<n;i++)

    {

        int xx,yy; scanf("%d%d",&xx,&yy);

        add(xx,yy); add(yy,xx);

    }

    f[1]=1; dfs1(1,1); memset(f,0,sizeof f); dfs2(1);

    for (int i=1;i<=n;i++) build_tree(root[c[i]],1,n,dfn[i],w[i]);

    while (q--)

    {

        int xx,yy; scanf("%s %d %d",s,&xx,&yy);

        if (s[0]=='C' && s[1]=='C')

        {

            int last=c[xx],now=yy;

            build_tree(root[now],1,n,dfn[xx],w[xx]);

            build_tree(root[last],1,n,dfn[xx],0);

            c[xx]=now;

        }

        else if (s[0]=='C' && s[1]=='W')

        {build_tree(root[c[xx]],1,n,dfn[xx],yy); w[xx]=yy;}

        else if (s[0]=='Q' && s[1]=='S')

        {printf("%d\n",qsum(xx,yy,c[xx]));}

        else if (s[0]=='Q' && s[1]=='M')

        {printf("%d\n",qmax(xx,yy,c[xx]));}

    }

return 0;

}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值