bzoj3531: [Sdoi2014]旅行(树链剖分+线段树)

题目传送门

解法:
据说叫动态开点。
需要用的点才建线段树。
那么最多只有二十万种不同的信息。。
跟主席树一样嘛。
YY就好了

代码实现:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct node {int x,y,next;}a[210000];int len,last[110000];
void ins(int x,int y) {len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int n,fa[110000],tot[110000],dep[110000],son[110000];
void pre_tree_node(int x) {
    tot[x]=1;son[x]=0;
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=fa[x]) {
            fa[y]=x;dep[y]=dep[x]+1;pre_tree_node(y);
            tot[x]+=tot[y];if(tot[son[x]]y])son[x]=y;
        }
    }
}
int z,ys[110000],top[110000];
void pre_tree_edge(int x,int tp) {
    ys[x]=++z;top[x]=tp;
    if(son[x]!=0)pre_tree_edge(son[x],tp);
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(y!=fa[x]&&y!=son[x]) pre_tree_edge(y,y);
    }
}
struct trnode {int lc,rc,c,mx;}tr[5100000];int trlen,rt[210000];
void change(int &now,int l,int r,int x,int k) {
    if(now==0)now=++trlen;
    if(l==r){tr[now].c=tr[now].mx=k;return ;}
    int mid=(l+r)/2;
    if(x<=mid)change(tr[now].lc,l,mid,x,k);
    else change(tr[now].rc,mid+1,r,x,k);
    tr[now].c=tr[tr[now].lc].c+tr[tr[now].rc].c;
    tr[now].mx=max(tr[tr[now].lc].mx,tr[tr[now].rc].mx);
}
int find_sum(int now,int l,int r,int x,int y) {
    if(now==0)return 0;if(l==x&&r==y)return tr[now].c;
    int mid=(l+r)/2;
    if(y<=mid)return find_sum(tr[now].lc,l,mid,x,y);
    else if(x>mid)return find_sum(tr[now].rc,mid+1,r,x,y);
    else return find_sum(tr[now].lc,l,mid,x,mid)+find_sum(tr[now].rc,mid+1,r,mid+1,y);
}
int find_max(int now,int l,int r,int x,int y) {
    if(now==0)return 0;if(l==x&&r==y)return tr[now].mx;
    int mid=(l+r)/2;
    if(y<=mid)return find_max(tr[now].lc,l,mid,x,y);
    else if(x>mid)return find_max(tr[now].rc,mid+1,r,x,y);
    else return max(find_max(tr[now].lc,l,mid,x,mid),find_max(tr[now].rc,mid+1,r,mid+1,y));
}
int solve_sum(int x,int y,int c) {
    int tx=top[x],ty=top[y],ans=0;
    while(tx!=ty) {
        if(dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);}
        ans+=find_sum(rt[c],1,n,ys[ty],ys[y]);y=fa[ty];ty=top[y];
    }if(dep[x]>dep[y]) swap(x,y);
    ans+=find_sum(rt[c],1,n,ys[x],ys[y]);return ans;
}
int solve_max(int x,int y,int c) {
    int tx=top[x],ty=top[y],ans=0;
    while(tx!=ty) {
        if(dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);}
        ans=max(ans,find_max(rt[c],1,n,ys[ty],ys[y]));y=fa[ty];ty=top[y];
    }if(dep[x]>dep[y]) swap(x,y);
    return max(ans,find_max(rt[c],1,n,ys[x],ys[y]));
}
char s[5];
int jh[110000],pj[110000];
int main() {
    int m;scanf("%d%d",&n,&m);len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=n;i++) scanf("%d%d",&pj[i],&jh[i]);
    for(int i=1;iint x,y;scanf("%d%d",&x,&y);ins(x,y);ins(y,x);}
    dep[1]=0;fa[1]=0;pre_tree_node(1);z=0;pre_tree_edge(1,1);trlen=0;
    for(int i=1;i<=n;i++) change(rt[jh[i]],1,n,ys[i],pj[i]);
    while(m--) {
        int x,y;scanf("%s%d%d",s+1,&x,&y);
        if(s[1]=='C') {
            if(s[2]=='C') {
                change(rt[jh[x]],1,n,ys[x],0);jh[x]=y;
                change(rt[jh[x]],1,n,ys[x],pj[x]);
            }else {pj[x]=y;change(rt[jh[x]],1,n,ys[x],pj[x]);}
        }else {
            if(s[2]=='S') printf("%d\n",solve_sum(x,y,jh[x]));
            else printf("%d\n",solve_max(x,y,jh[x]));
        }
    }
    return 0;
}

你可能感兴趣的:(树链剖分,线段树,BZOJ)