[SDOI2014]旅行(D12 树链剖分+动态开点)

题目描述

给一棵树,每个节点有一种颜色和权值,有四种操作:将一个节点颜色改变,讲一个节点权值改变,求一上路径上某种颜色的的点的权值和,求一条路径上某种颜色的点的最大权值

 

N,Q < =105 , C < =105

 

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

 

 

题解

一条路径上有多种颜色,不好同时维护,所以考虑一种暴力做法:树链剖分,对于每种颜色建一棵线段树,不是这个颜色在这棵线段树权值为0,因为查询是一种颜色就可以在一棵线段树上查,是可行的。

但如果将线段树开满的话是肯定会MLE的,考虑动态开点,初始会有nlgn个点,每次操作最多增加lgn个点,所以就可以保证空间。

要注意的是改变点权时,数组那里也要改,因为改颜色时要用到,不过也可以在改颜色的时候记录权值。

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
int n,m;
int a[maxn],co[maxn];
int size[maxn],dep[maxn],fa[maxn],son[maxn];
int top[maxn],id[maxn],cnt;
int num,root[maxn],ls[maxn<<5],rs[maxn<<5],sum[maxn<<5],mx[maxn<<5];
vector<int>e[maxn];

template<class T>void read(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
}

int max(int x,int y){return x>y ? x : y ;}

void dfs(int u){
    size[u]=1;
    for(unsigned int i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(v==fa[u]) continue;
        fa[v]=u;
        dep[v]=dep[u]+1;
        dfs(v);
        size[u]+=size[v];
        if(size[son[u]]<size[v]) son[u]=v;
    }
}

void update(int rt){
    sum[rt]=sum[ls[rt]]+sum[rs[rt]];
    mx[rt]=max(mx[ls[rt]],mx[rs[rt]]);
}

void modify(int &rt,int l,int r,int pos,int val){
    if(!rt) rt=++num;
    if(l==r){
        mx[rt]=sum[rt]=val;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) modify(ls[rt],l,mid,pos,val);
    else modify(rs[rt],mid+1,r,pos,val);
    update(rt);
}

void dfs(int u,int tp){
    id[u]=++cnt;
    top[u]=tp;
    modify(root[co[u]],1,n,cnt,a[u]);
    if(!son[u]) return ;
    dfs(son[u],tp);
    for(unsigned int i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(v==fa[u]||v==son[u]) continue;
        dfs(v,v);
    }
}

int querysum(int rt,int l,int r,int a_l,int a_r){
    if(a_l<=l&&r<=a_r) return sum[rt];
    int ret=0,mid=(l+r)>>1;
    if(a_l<=mid) ret+=querysum(ls[rt],l,mid,a_l,a_r);
    if(mid<a_r) ret+=querysum(rs[rt],mid+1,r,a_l,a_r);
    return ret;
}

int querysum(int rt,int x,int y){
    int ret=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        ret+=querysum(rt,1,n,id[top[y]],id[y]);
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ret+=querysum(rt,1,n,id[x],id[y]);
    return ret;
}

int querymax(int rt,int l,int r,int a_l,int a_r){
    if(a_l<=l&&r<=a_r) return mx[rt];
    int ans=0,mid=(l+r)>>1;
    if(a_l<=mid) ans=max(ans,querymax(ls[rt],l,mid,a_l,a_r));
    if(mid<a_r) ans=max(ans,querymax(rs[rt],mid+1,r,a_l,a_r));
    return ans;
}

int querymax(int rt,int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        ans=max(ans,querymax(rt,1,n,id[top[y]],id[y]));
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans=max(ans,querymax(rt,1,n,id[x],id[y]));
    return ans;
}

int main(){
    read(n);read(m);
    for(int i=1;i<=n;i++) read(a[i]),read(co[i]);
    for(int i=1;i<n;i++){
        int x,y;
        read(x);read(y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dep[1]=1;
    dfs(1);
    dfs(1,1);
    for(int i=1;i<=m;i++){
        char s[2];int x,y;
        scanf("%s",s);
        read(x);read(y);
        if(s[1]=='C'){//城市x改信y教 
            modify(root[co[x]],1,n,id[x],0);
            modify(root[y],1,n,id[x],a[x]);
            co[x]=y;
        }
        else if(s[1]=='W'){//城市x评级调为y 
            modify(root[co[x]],1,n,id[x],y);
            a[x]=y;///注意 
        }
        else if(s[1]=='S'){//x到y的评价总和 
            printf("%d\n",querysum(root[co[x]],x,y));
        }
        else {//x到y的评价最大值 
            printf("%d\n",querymax(root[co[x]],x,y));
        }
    }
}
View Code

 

posted @ 2019-07-25 20:59  _JSQ  阅读(154)  评论(0编辑  收藏  举报