bzoj 3531 旅行 (树剖+动态建树)

3531: [Sdoi2014]旅行

Time Limit: 40 Sec   Memory Limit: 512 MB
Submit: 2990   Solved: 1318
[ Submit][ Status][ Discuss]

Description

 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
    在S国的历史上常会发生以下几种事件:
”CC x c”:城市x的居民全体改信了c教;
”CW x w”:城市x的评级调整为w;
”QS x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;
”QM x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
    由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。    为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Input

    输入的第一行包含整数N,Q依次表示城市数和事件数。
    接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的
评级和信仰。
    接下来N-1行每行两个整数x,y表示一条双向道路。
    接下来Q行,每行一个操作,格式如上所述。

Output

    对每个QS和QM事件,输出一行,表示旅行者记下的数字。

Sample Input

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

Sample Output

8
9
11
3

HINT

N,Q < =10^5    , C < =10^5


 数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。


分析:由于每次查询只是查询这条路径上与起点宗教相同的权值后和或最大值,那么可以对每个宗教动态建树,这样查询和维护时只需要对每个宗教进行树剖

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+200;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
struct edge
{
    int v,next;
} e[N<<1];
int siz[N],tid[N],rnk[N],top[N],faz[N],son[N],dep[N];
int first[N],tot,cnt,sizen,w[N],c[N],roots[N];
struct node
{
    int lson,rson,sum,maxx;

} t[N*35];
void add(int u,int v)
{
    e[tot].v=v;
    e[tot].next=first[u];
    first[u]=tot++;
}
void dfs1(int u,int father,int depth)
{
    siz[u]=1;
    dep[u]=depth;
    faz[u]=father;
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v;
        if(v!=father)
        {
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
}
void dfs2(int u,int t)
{
    tid[u]=++cnt;
    rnk[cnt]=u;
    top[u]=t;
    if(son[u]==-1)
        return;
    dfs2(son[u],t);
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v;
        if(v!=faz[u]&&v!=son[u])
            dfs2(v,v);
    }
}
void pushup(int rt)
{
    t[rt].sum=t[t[rt].lson].sum+t[t[rt].rson].sum;
    t[rt].maxx=max(t[t[rt].lson].maxx,t[t[rt].rson].maxx);
}
void update(int q,int l,int r,int &rt,int w)
{
    if(!rt)rt=++sizen;
    if(l==r)
    {
        t[rt].maxx=t[rt].sum=w;
        return;
    }
    int m=(l+r)>>1;
    if(q<=m)update(q,l,m,t[rt].lson,w);
    else update(q,m+1,r,t[rt].rson,w);
    pushup(rt);
}
int q1(int ql,int qr,int l,int r,int rt)
{
    if(ql<=l&&r<=qr)return t[rt].maxx;
    int m=(l+r)>>1,ans=0;
    if(ql<=m)ans=max(ans,q1(ql,qr,l,m,t[rt].lson));
    if(qr>m)ans=max(ans,q1(ql,qr,m+1,r,t[rt].rson));
    return ans;
}
int q2(int ql,int qr,int l,int r,int rt)
{
    if(ql<=l&&r<=qr)return t[rt].sum;
    int m=(l+r)>>1,ans=0;
    if(ql<=m)ans+=q2(ql,qr,l,m,t[rt].lson);
    if(qr>m)ans+=q2(ql,qr,m+1,r,t[rt].rson);
    return ans;
}
int query_path(int u,int v,char op)
{
    int ans=0,color=c[u];
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])
        swap(u,v);
        if(op=='M')ans=max(ans,q1(tid[top[u]],tid[u],1,cnt,roots[color]));
        else ans+=q2(tid[top[u]],tid[u],1,cnt,roots[color]);
        u=faz[top[u]];
    }
    if(tid[u]>tid[v])swap(u,v);
    if(op=='M')ans=max(ans,q1(tid[u],tid[v],1,cnt,roots[color]));
        else ans+=q2(tid[u],tid[v],1,cnt,roots[color]);

        return ans;
}
void init()
{
    mem(son,-1);
    mem(first,-1);
    tot=cnt=sizen=0;
}
int main()
{
    int n,q,u,v;
    char op[5];
    init();
    scanf("%d%d",&n,&q);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&w[i],&c[i]);
    for(int i=2; i<=n; i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(1,1,1);
    dfs2(1,1);
    for(int i=1; i<=n; i++)
        update(tid[i],1,n,roots[c[i]],w[i]);

    while(q--)
    {
        scanf("%s%d%d",op,&u,&v);
        if(op[0]=='C')
        {
            if(op[1]=='C')
            {
                update(tid[u],1,n,roots[c[u]],0);
                c[u]=v;
                update(tid[u],1,n,roots[v],w[u]);
            }
            else
            {
                update(tid[u],1,n,roots[c[u]],v);
                w[u]=v;
            }
        }
        else
        printf("%d\n",query_path(u,v,op[1]));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值