[BZOJ3531][SDOI2014]旅行(链剖+线段树动态开点)

题目描述

传送门

题解

神烦一下午= =
不过这道题挺好的,让我真正明白了动态开点。和主席树有区别。之前的思路是对的,但是动态开点一直写挂,链剖什么的还是没问题的。
链剖是一眼就能看出来的;需要给每一个宗教建一棵线段树,线段树用动态开点。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=1e5+5;
const int max_sz=18;
const int max_e=max_n*2;
const int max_tree=max_n*5;

int n,q,x,y,C,W;
int w[max_n],c[max_n];
char s[5];
int tot,point[max_n],next[max_e],v[max_e];
int size[max_n],father[max_n],son[max_n],h[max_n];
int top[max_n],num[max_n];
int root[max_n],last[max_n];
int N,sz;

struct hp{
    int sum,max;
    int l,r;
};
hp tree[10*max_n*max_sz];

inline void addedge(int x,int y){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline void dfs_1(int x,int fa,int dep){
    size[x]=1; h[x]=dep; father[x]=fa;
    int maxson=0;
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        dfs_1(v[i],x,dep+1);
        size[x]+=size[v[i]];
        if (size[v[i]]>maxson){
            maxson=size[v[i]];
            son[x]=v[i];
        }
      }
}

inline void dfs_2(int x,int fa){
    if (son[fa]!=x) top[x]=x;
    else top[x]=top[fa];

    num[x]=++N;

    if (son[x]) dfs_2(son[x],x);
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa&&v[i]!=son[x])
        dfs_2(v[i],x);
}

inline void update(int now){
    tree[now].sum=tree[ tree[now].l ].sum+tree[ tree[now].r ].sum;
    tree[now].max=max(tree[ tree[now].l ].max,tree[ tree[now].r ].max);
}

inline void build(int &now,int l,int r,int x,int v){
    int mid=(l+r)>>1;

    if (!now) now=++sz;
    if (l==r){
        tree[now].sum=v;
        tree[now].max=v;
        return;
    } 
    if (x<=mid)
      build(tree[now].l,l,mid,x,v);
    else
      build(tree[now].r,mid+1,r,x,v);

    update(now);
}

inline int query_sum(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange) 
      return tree[now].sum;
    if (lrange<=mid)
      ans+=query_sum(tree[now].l,l,mid,lrange,rrange);
    if (mid+1<=rrange)
      ans+=query_sum(tree[now].r,mid+1,r,lrange,rrange);
    return ans;
}

inline int query_max(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=0;
    if (lrange<=l&&r<=rrange)
      return tree[now].max;
    if (lrange<=mid)
      ans=max(ans,query_max(tree[now].l,l,mid,lrange,rrange));
    if (mid+1<=rrange)
      ans=max(ans,query_max(tree[now].r,mid+1,r,lrange,rrange));
    return ans;
}

int main(){
// freopen("input.txt","r",stdin);
    scanf("%d%d",&n,&q);

    for (int i=1;i<=n;++i)
      scanf("%d%d",&w[i],&c[i]);

    for (int i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }

    dfs_1(1,0,1);
    dfs_2(1,0);

    for (int i=1;i<=n;++i)
      build(root[c[i]],1,N,num[i],w[i]);

    for (int i=1;i<=q;++i){
        scanf("%s",s);
        if (s[0]=='C'){
            if (s[1]=='C'){
                scanf("%d%d",&x,&C);

                build(root[c[x]],1,N,num[x],0);
                build(root[C],1,N,num[x],w[x]);
                c[x]=C;

            }
            if (s[1]=='W'){
                scanf("%d%d",&x,&W);

                build(root[c[x]],1,N,num[x],W);
                w[x]=W;
            }
        }
        else{
            if (s[1]=='S'){
                scanf("%d%d",&x,&y);
                int st=c[x];

                int ans=0;
                int f1=top[x],f2=top[y];
                while (f1!=f2){
                    if (h[f1]<h[f2]){
                        swap(x,y);
                        swap(f1,f2);
                    }
                    ans+=query_sum(root[st],1,N,num[f1],num[x]);
                    x=father[f1];
                    f1=top[x];
                }
                if (num[x]>num[y]) swap(x,y);
                ans+=query_sum(root[st],1,N,num[x],num[y]);

                printf("%d\n",ans);
            }
            if (s[1]=='M'){
                scanf("%d%d",&x,&y);
                int st=c[x];

                int ans=0;
                int f1=top[x],f2=top[y];
                while (f1!=f2){
                    if (h[f1]<h[f2]){
                        swap(x,y);
                        swap(f1,f2);
                    }
                    ans=max(ans,query_max(root[st],1,N,num[f1],num[x]));
                    x=father[f1];
                    f1=top[x];
                }
                if (num[x]>num[y]) swap(x,y);
                ans=max(ans,query_max(root[st],1,N,num[x],num[y]));

                printf("%d\n",ans);
            }
        }
    }
}

总结

动态开点的方法注意一下;每一步想清楚。

你可能感兴趣的:(线段树,bzoj,SDOI,链剖,动态开点)