bzoj3531 [Sdoi2014]旅行

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,并记下了途中留宿过的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

N,Q < =10^5 , C < =10^5
数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于10^4 的正整数,且宗教值不大于C。

Solution


我大概只会做数据结构题la

裸的树链剖分,如果只有一种颜色就是一棵线段树,现在有多种颜色就开多种线段树

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=200005;
const int E=400005;

struct edge {int x,y,next;} e[E];
struct treeNode {int l,r,sum,max;} t[N*51];

int root[N],w[N],c[N],tot;
int bl[N],fa[N],pos[N];
int size[N],dep[N];
int ls[N],edCnt;
int n,m;

void add_edge(int x,int y) {
    e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

void dfs1(int now) {
    size[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa[now]) continue;
        dep[e[i].y]=dep[now]+1;
        fa[e[i].y]=now;
        dfs1(e[i].y);
        size[now]+=size[e[i].y];
    }
}

void dfs2(int now,int up) {
    int mx=0; pos[now]=++pos[0]; bl[now]=up;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y!=fa[now]&&size[e[i].y]>size[mx]) mx=e[i].y;
    }
    if (!mx) return ;
    dfs2(mx,up);
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y!=fa[now]&&e[i].y!=mx) dfs2(e[i].y,e[i].y);
    }
}

void modify(int &now,int tl,int tr,int x,int v) {
    if (!now) t[now=++tot]=(treeNode) {0,0,0,0};
    if (tl==tr) {
        t[now].sum=t[now].max=v;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(t[now].l,tl,mid,x,v);
    else modify(t[now].r,mid+1,tr,x,v);
    t[now].max=std:: max(t[t[now].l].max,t[t[now].r].max);
    t[now].sum=t[t[now].l].sum+t[t[now].r].sum;
}

int query(int now,int tl,int tr,int l,int r,int opt) {
    if (tl==l&&tr==r) return (opt)?(t[now].sum):(t[now].max);
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(t[now].l,tl,mid,l,r,opt);
    if (l>mid) return query(t[now].r,mid+1,tr,l,r,opt);
    int qx=query(t[now].l,tl,mid,l,mid,opt);
    int qy=query(t[now].r,mid+1,tr,mid+1,r,opt);
    return (opt)?(qx+qy):(std:: max(qx,qy));
}

void solve(int x,int y,int col,int opt) {
    int ret=0,tmp;
    while (bl[x]!=bl[y]) {
        if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
        tmp=query(root[col],1,n,pos[bl[x]],pos[x],opt);
        ret=(opt)?(ret+tmp):(std:: max(ret,tmp));
        x=fa[bl[x]];
    }
    if (pos[y]<pos[x]) std:: swap(x,y);
    tmp=query(root[col],1,n,pos[x],pos[y],opt);
    ret=(opt)?(ret+tmp):(std:: max(ret,tmp));
    printf("%d\n", ret);
}

int main(void) {
    freopen("data.in","r",stdin);
    scanf("%d%d",&n,&m);
    rep(i,1,n) scanf("%d%d",&w[i],&c[i]);
    rep(i,2,n) {
        int x,y; scanf("%d%d",&x,&y);
        add_edge(x,y);
    }
    dep[1]=1; dfs1(1);
    dfs2(1,1);
    rep(i,1,n) modify(root[c[i]],1,n,pos[i],w[i]);
    while (m--) {
        char opt[3]; scanf("%s",opt);
        int x,y; scanf("%d%d",&x,&y);
        if (opt[1]=='C') {
            modify(root[c[x]],1,n,pos[x],0);
            c[x]=y;
            modify(root[c[x]],1,n,pos[x],w[x]);
        } else if (opt[1]=='W') {
            w[x]=y;
            modify(root[c[x]],1,n,pos[x],w[x]);
        } else if (opt[1]=='S') {
            solve(x,y,c[x],1);
        } else if (opt[1]=='M') {
            solve(x,y,c[x],0);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值