[BZOJ3531][Sdoi2014]旅行 做题笔记

·· / ·– ·· ·-·· ·-·· / ·–· · ·-· ··· ·· ··· - / ··- -· - ·· ·-·· / ·· / ·– ·· -·
题目来源http://www.lydsy.com/JudgeOnline/problem.php?id=3531

这是一道非常有价值的树剖。
对于每个飞天面条神教,建立一颗线段树进行维护,查询时,在对应的飞天面条神教对应的线段树进行查询。
如果一个城市信了其他的飞天面条神教,那么把它从自己的面条神教对应的线段树中抹去,再在新面条教的线段树中加上权值。
c <= 100000 真的不会爆吗?
不会,因为这里的线段树是动态加点的。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=110000,M=10000009;
int size[N],son[N],fa[N],dep[N],w[N],top[N],id[M];
int ls[M],rs[M],mx[M],sum[M],root[N];
int ver[N<<1],nxt[N<<1],head[N];
int ww[N],cc[N];
int n,m,cnt=0,tot=1,ne=0;
int read () {
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}
void add (int u,int v) {
    ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
void dfs_1 (int u,int from) {
    size[u]=1;son[u]=0;fa[u]=from;
    for (int i=head[u];i;i=nxt[i]) {
        int v=ver[i];
        if (v==fa[u]) continue;
        dep[v]=dep[u]+1; fa[v]=u;
        dfs_1(v,u);size[u]+=size[v];
        if (!son[u]||size[son[u]]<size[v]) son[u]=v;
    }
}
void dfs_2 (int u,int st) {
    w[u]=++cnt;top[u]=st;id[cnt]=u;
    if (son[u]) dfs_2(son[u],st);
    for (int i=head[u];i;i=nxt[i]) {
        int v=ver[i];
        if (v==son[u]||v==fa[u]) continue;
        dfs_2(v,v);
    }
}
void update (int x) {
    mx[x]=max(mx[ls[x]],mx[rs[x]]);
    sum[x]=sum[ls[x]]+sum[rs[x]];
}
void change (int &k,int l,int r,int x,int num) {
    if (!k) k=++ne;//动态加点
    if (l==r) { mx[k]=sum[k]=num;return; }
    int mid=(l+r)>>1;
    if (x<=mid) change(ls[k],l,mid,x,num);
    else change(rs[k],mid+1,r,x,num);
    update(k);
}
int askmx (int k,int l,int r,int x,int y) {
    if (!k) return 0;//防止访问到没拓展过的点
    if (x<=l&&r<=y) return mx[k];
    int ans=0,mid=(l+r)>>1;
    if (x<=mid) ans=max(askmx(ls[k],l,mid,x,y),ans);
    if (y>mid) ans=max(askmx(rs[k],mid+1,r,x,y),ans);
    return ans;
}
int asksum (int k,int l,int r,int x,int y) {
    if (!k) return 0;
    if (x<=l&&r<=y) return sum[k];
    int ans=0,mid=(l+r)>>1;
    if (x<=mid) ans+=asksum(ls[k],l,mid,x,y);
    if (y>mid) ans+=asksum(rs[k],mid+1,r,x,y);
    return ans;
}
int getmx (int cc,int x,int y) {
    int mx=0;
    while (top[x]!=top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        mx=max(mx,askmx(root[cc],1,n,w[top[x]],w[x]));
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    mx=max(mx,askmx(root[cc],1,n,w[x],w[y]));
    return mx;
}
int getsum (int cc,int x,int y) {
    int sum=0;
    while (top[x]!=top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        sum+=asksum(root[cc],1,n,w[top[x]],w[x]);
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    sum+=asksum(root[cc],1,n,w[x],w[y]);
    return sum;
}
int main () {
    int u,v,x,y;
    char str[5];
    n=read(); m=read();
    for (int i=1;i<=n;i++) ww[i]=read(),cc[i]=read();
    for (int i=1;i<n;i++) {
        u=read(); v=read();
        add(u,v); add(v,u);
    }
    dfs_1(1,0); dfs_2(1,1);
    for (int i=1;i<=n;i++) 
        change(root[cc[i]],1,n,w[i],ww[i]);
    for (int i=1;i<=m;i++) {
        scanf("%s",str);
        x=read(); y=read();
        if (str[0]=='C') {
            if (str[1]=='C') {
                change(root[cc[x]],1,n,w[x],0);
                cc[x]=y;
                change(root[cc[x]],1,n,w[x],ww[x]);
            }
            else change(root[cc[x]],1,n,w[x],y),ww[x]=y;
        }
        else {
            if (str[1]=='S') printf("%d\n",getsum(cc[x],x,y));
            else printf("%d\n",getmx(cc[x],x,y));
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值