[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章