BZOJ 3531 : [Sdoi2014]旅行

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

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

思路:

  由于是在树上维护点与点之间路径的信息,考虑对树进行链剖再架线段树来解决。分析发现,我们只需要对每一种信仰开一颗线段树即可维护全部信息。当某城市更改信仰时,只需要把它从原来那颗树上归零,再在新树上加入,考虑动态开点即可。对于每种宗教维护区间合和区间最大值。代码如下

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cctype>
#define im int mid = (l + r) >> 1
using namespace std;
const int N = 200000;
int son[N],siz[N],fa[N],top[N],dep[N];
int idx[N],cnt2;
int to[N<<1],next[N<<1],val[N],head[N],b[N],cnt1;
int n,m;
int tr[N],
ls[((N<<3)+(N<<1))<<1], 
rs[((N<<3)+(N<<1))<<1], 
sum[((N<<3)+(N<<1))<<1], 
maxn[((N<<3)+(N<<1))<<1];
class ReadIn {
    private:
    inline char nc() {
        static char buf[100000], *p1, *p2;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    public:
    inline int read() {
        int x=0;char ch=nc();
        while(!isdigit(ch))ch=nc();
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=nc();}
        return x;
    }
    inline char getc() {
        char ch=nc();
        while(isspace(ch))ch=nc();
        return ch;
    }
}Rd;
class SegmentTree {
    public :
        void change(int l,int r,int x,int y,int &p) {
            if(!p)p=++cnt2;
            if(l==r) {
                maxn[p]=sum[p]=y;
                return;
            }
            int mid = (l+r) >> 1;
            if(x<=mid) change(l,mid,x,y,ls[p]);
            else change(mid+1,r,x,y,rs[p]);
            sum[p]=sum[ls[p]]+sum[rs[p]];
            maxn[p]=max(maxn[ls[p]],maxn[rs[p]]);
    }
    int qsum(int l,int r,int x,int y,int &p) {
        if(!p)return 0;
        if(x<=l&&r<=y) {
            return sum[p];
        }
        int mid= (l+r) >> 1;
        int re = 0;
        if(x<=mid) re+=qsum(l,mid,x,y,ls[p]);
        if(y>mid) re+=qsum(mid+1,r,x,y,rs[p]);
        return re;
    }
    int qmax(int l,int r,int x,int y,int &p) {
        if(!p)return 0;
        if(x<=l&&r<=y) {
            return maxn[p];
        }
        int mid= (l+r) >> 1;
        int re = 0;
        if(x<=mid) re=max(re,qmax(l,mid,x,y,ls[p]));
        if(y>mid) re=max(re,qmax(mid+1,r,x,y,rs[p]));
        return re;
    }
 
}Tr;
class TreeChainDissection {
    public :
    void dfs1(int p) {
        dep[p]=dep[fa[p]]+1;
        siz[p]=1;
        for (int i = head[p];i; i = next[i] ) {
            if(to[i] != fa[p]) {
                fa[to[i]]=p;
                dfs1(to[i]);
                siz[p]+=siz[to[i]];
                if(siz[to[i]]>siz[son[p]])
                    son[p]=to[i];
            }
        }
    }
    void dfs2(int p,int t) {
        idx[p]=++cnt2;
        top[p]=t;
        if(son[p]) dfs2(son[p],t);
        for(int i=head[p];i;i=next[i])
            if(to[i]!=fa[p]&&to[i]!=son[p])
                dfs2(to[i],to[i]);
    }
    int lcas(int x,int y) {
        int re = 0, root = b[x];
        while(top[x] != top[y]) {
            if(dep[top[x]]>dep[top[y]]) swap(x,y);
            re+=Tr.qsum(1,n,idx[top[y]],idx[y],tr[root]);
            y=fa[top[y]];
        }
        if(dep[x]<dep[y])swap(x,y);
        re+=Tr.qsum(1,n,idx[y],idx[x],tr[root]);
        return re;
    }
    int lcam(int x,int y) {
        int re=0, root = b[x];
        while(top[x]!=top[y]) {
            if(dep[top[x]]>dep[top[y]]) swap(x,y);
            re = max(re,Tr.qmax(1,n,idx[top[y]],idx[y],tr[root]));
            y=fa[top[y]];
        }
        if(dep[x]<dep[y])swap(x,y);
        re = max(re,Tr.qmax(1,n,idx[y],idx[x],tr[root]));
        return re;
    }
}Tcd;
class Pre {
    private :
    inline void add_edge(int a,int b) {
        to[++cnt1] = b;
        next[cnt1] = head[a];
        head[a] = cnt1;
 
        to[++cnt1] = a;
        next[cnt1] = head[b];
        head[b] = cnt1;
    }
    public :
    void init() {
        n=Rd.read(),m=Rd.read();
        int i,x,y;
        for(i=1 ;i <=n; i++) val[i] = Rd.read(),b[i] = Rd.read();
        for(i=1 ;i < n; i++) {
            x=Rd.read(),y=Rd.read();
            add_edge(x,y);
        }
        Tcd.dfs1(1);
        Tcd.dfs2(1,1);
        for(i=1 ;i <=n; i++) {
            Tr.change(1,n,idx[i],val[i],tr[b[i]]);
        }
 
    }
}Pr;
void solve() {
     
    int i,x,y;
    char opt;
    for(i=1;i<=m;i++) {
        opt=Rd.getc();
        opt=Rd.getc();
        x = Rd.read();
        y = Rd.read();
        if(opt=='C') {
            Tr.change(1,n,idx[x],0,tr[b[x]]);
            b[x]=y;
            Tr.change(1,n,idx[x],val[x],tr[b[x]]);
        }
        else if(opt=='S'){
            int ans=Tcd.lcas(x,y);
            printf("%d\n",ans);
        }
        else if(opt=='W') {
            Tr.change(1,n,idx[x],y,tr[b[x]]);
            val[x] = y;
        }
        else {
            int ans=Tcd.lcam(x,y);
            printf("%d\n", ans);
        }
    }
}
int main() {
    Pr.init();
    solve();
}

 欢迎来原博客看看 >原文地址<

posted @ 2018-05-24 13:48  TOBICHI_ORIGAMI  阅读(59)  评论(0编辑  收藏  举报