[SDOI2014]旅行

[SDOI2014]旅行

题意:

n个城市,n-1条边,任意两个城市互通,每个城市有所信奉的宗教和城市评级,有四种指令:
1.将城市x的居民改信为c教
2.将城市x的评级调整为w
3.统计x到y,路上所有的城市的评级综合(被记录的城市宗教必须与x和y相同,数据保证x和y宗教一致)
4.统计x到y,路上的评级最大值(被记录的城市要求与上述一样)
按照要求输出答案

题解:

参考文章为洛谷题解的第一篇(文章好像显示不可查看
洛谷的题解第一页
题目比较麻烦,因为有两个要维护的量,如果是一个还好说,对于树上维护那肯定是树链剖分,现在关键就是如果确保只统计同一宗教的评级
我们可以将每个宗教建立一个线段树这样分开维护不久ok了,但是内存会超,那我们就主席树,动态开点
现在讲讲具体细节:
动态开点就不说了
我们讲讲改信其他教和评级调整如何实现?
原本是a教,改成c教
那我们就先到a教的线段树中找到这个点,然后将其评级和评级最大值都清零(可以理解为删除这个点,但实际还存在只是没有值),然后到c教里将这个点再加上,并加上评级
注意:在删除过程中记得要pushup(root),你删除一个点后,整个数的最大值和权值和发生了改变,所以即可更新到根节点
评级更新也是差不多,先把原本评级删去,再附上新评级
剩下都是树链剖分常规操作
代码有详细注释

代码:

#include
using namespace std;
struct node{
     
    int to,next;
}g[1000000];
int tot,n,m,cnt,w[100004],zj[100004],len,head[100004],dep[100004],wson[100004],top[100004],tpos[100004],pre[100004],fa[100004],size[100004];
inline void made(int from,int to){
     
    g[++tot].to=to;
    g[tot].next=head[from];
    head[from]=tot;
}
inline void dfs1(int rt,int ff){
     
    fa[rt]=ff;
	dep[rt]=dep[ff]+1;
	size[rt]=1;
    for (int i=head[rt];i;i=g[i].next){
     
        int v=g[i].to;
        if (v==ff) continue;
        dfs1(v,rt);
        size[rt]+=size[v];
        if (!wson[rt]||size[wson[rt]]<size[v]) wson[rt]=v;
    }
}
inline void dfs2(int rt,int tops){
     
    tpos[rt]=++cnt;
	pre[cnt]=rt;
	top[rt]=tops;
    if (wson[rt]) dfs2(wson[rt],tops);
    for (int i=head[rt];i;i=g[i].next){
     
        int v=g[i].to;
        if (v==fa[rt]||v==wson[rt]) continue;
        dfs2(v,v);
    }
}
int root[100004];
struct Node{
     
    int l,r,max,tot;
}tree[20000110];
inline void update(int &rt,int w,int l,int r,int pos){
     //加点操作 
    if (!rt) rt=++len;//动态开点 
    tree[rt].max=max(tree[rt].max,w);
	tree[rt].tot+=w;
    if (l==r) return;
	int mid=(l+r)/2;
    if (mid>=pos) update(tree[rt].l,w,l,mid,pos);
    else update(tree[rt].r,w,mid+1,r,pos);
}
inline void remove(int &rt,int l,int r,int pos){
     //删点操作 
    if (l==r){
      
		tree[rt].tot=0;
		tree[rt].max=0;
		return; 
	}
    int mid=(l+r)/2;
    if (mid>=pos) remove(tree[rt].l,l,mid,pos);
    else remove(tree[rt].r,mid+1,r,pos);
    tree[rt].tot=tree[tree[rt].l].tot+tree[tree[rt].r].tot;
    tree[rt].max=max(tree[tree[rt].l].max,tree[tree[rt].r].max);
}

inline int querytot(int rt,int lb,int rb,int l,int r){
     //查询区间总值 
    if (r<lb||l>rb) return 0;
    if (r>=rb&&l<=lb) return tree[rt].tot;
    int mid=(lb+rb)/2;
    return querytot(tree[rt].l,lb,mid,l,r)+querytot(tree[rt].r,mid+1,rb,l,r);
}
inline int querymax(int rt,int lb,int rb,int l,int r){
     //查询区间最大值 
    if (r<lb||l>rb) return 0;
    if (r>=rb&&l<=lb) return tree[rt].max;
    int mid=(lb+rb)/2;
    return max(querymax(tree[rt].l,lb,mid,l,r),querymax(tree[rt].r,mid+1,rb,l,r));
}
//--上面为动态开点线段树,下面为树链剖分 
inline int sigmax(int u,int v,int zj){
     
    int ans=0;
    while (top[u]!=top[v]){
     
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        ans=max(ans,querymax(root[zj],1,n,tpos[top[u]],tpos[u]));
        u=fa[top[u]];
    }
    if (dep[u]<dep[v]) swap(u,v);
    ans=max(ans,querymax(root[zj],1,n,tpos[v],tpos[u]));
    return ans;
}
inline int sigtot(int u,int v,int zj){
     
    int ans=0;
    while (top[u]!=top[v]){
     
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        ans=ans+querytot(root[zj],1,n,tpos[top[u]],tpos[u]);
        u=fa[top[u]];
    }
    if (dep[u]<dep[v]) swap(u,v);
    ans=ans+querytot(root[zj],1,n,tpos[v],tpos[u]);
    return ans;
}
char s[100];
int main(){
     
    len=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
     
        scanf("%d%d",&w[i],&zj[i]);
    }
    int x,y;
    for (int i=1;i<n;i++){
     
        scanf("%d%d",&x,&y);
        made(x,y);
		made(y,x);
    }
    dfs1(1,0);
	dfs2(1,1);
    for (int i=1;i<=n;i++){
     
        update(root[zj[i]],w[i],1,n,tpos[i]);
    }		
    while (m--){
     
        scanf("%s",s);
		scanf("%d%d",&x,&y);
        switch (s[1]){
     
            case 'C':{
     //改信了c教 
                remove(root[zj[x]],1,n,tpos[x]);
                zj[x]=y;
                update(root[zj[x]],w[x],1,n,tpos[x]);
                
                break;
            }
            case 'W':{
     //城市x的评级调整为w;
                remove(root[zj[x]],1,n,tpos[x]);
                w[x]=y;
                update(root[zj[x]],w[x],1,n,tpos[x]);
                break;
            }
            case 'S':{
     //记录评级总和 
                printf("%d\n",sigtot(x,y,zj[x]));
                break;
            } 
            case 'M':{
     //记录评级最大值 
                printf("%d\n",sigmax(x,y,zj[x]));
                break;
            }
        }
    }
    return 0;
}

你可能感兴趣的:([SDOI2014]旅行)