[XJOI]黑白樹

題目大意

有一棵以1 爲根的n 個點的樹,每個點有黑白兩種顏色以及一個權值(一開始都是零)。
q 個操作,有以下兩種:
  選定一個點x ,將所有黑色節點y 的權值加上lca(x,y)
 x 號點的顏色反色
在所有操作執行完後,請輸出所有點的權值。

1n,q5×104


題目分析

將每個點的編號都和父親編號做差,這樣一個點對權值的貢獻就可以看成起到根路徑的和再乘上這個點作爲1 操作的點的祖先的次數。
考慮使用數據結構維護路徑上的一個計數器,計算一條路徑的編號差與次數乘積之和。因爲每次只是修改黑色節點,我們使用差分的思想,當一個點變成黑色點的時候我們把它的答案減去其到根路徑的權值和,變成白色時加上這個權值和。最後再處理沒有變回白色的點。
數據結構使用LCT 即可做到O(nlogn)


代碼實現

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>

using namespace std;

typedef long long LL;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

void write(LL x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int N=50050;
const int E=N<<1;

int fa[N],last[N];
int tov[E],nxt[E];
bool col[N];
LL ans[N];
int n,q,tot;

namespace link_cut_tree
{
    int fa[N],size[N],par[N],tag[N],val[N];
    LL sum[N],f[N],valsum[N];
    stack<int> st;
    int son[N][2];
    bool mark[N];

    bool side(int x){return son[fa[x]][1]==x;}

    void update(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1,valsum[x]=valsum[son[x][0]]+valsum[son[x][1]]+val[x],sum[x]=sum[son[x][0]]+sum[son[x][1]]+f[x];}

    void ADD(int x,int delta){tag[x]+=delta,f[x]+=1ll*delta*val[x],sum[x]+=1ll*delta*valsum[x];}

    void R(int x){swap(son[x][0],son[x][1]),mark[x]^=1;}

    void clear(int x)
    {
        if (mark[x])
        {
            if (son[x][0]) R(son[x][0]);
            if (son[x][1]) R(son[x][1]);
            mark[x]=0;
        }
        if (tag[x])
        {
            if (son[x][0]) ADD(son[x][0],tag[x]);
            if (son[x][1]) ADD(son[x][1],tag[x]);
            tag[x]=0;
        }
    }

    void rotate(int x)
    {
        int y=fa[x];bool s=side(x);
        if (fa[y]) son[fa[y]][side(y)]=x;
        if (son[x][s^1]) fa[son[x][s^1]]=y;
        son[y][s]=son[x][s^1],son[x][s^1]=y;
        fa[x]=fa[y],fa[y]=x;
        if (par[y]) par[x]=par[y],par[y]=0;
        update(y),update(x);
    }

    void pushdown(int x,int y)
    {
        for (;x!=y;st.push(x),x=fa[x]);
        for (;!st.empty();clear(st.top()),st.pop());
    }

    void splay(int x,int y)
    {
        for (pushdown(x,y);fa[x]!=y;rotate(x))
            if (fa[fa[x]]!=y)
                if (side(x)==side(fa[x])) rotate(fa[x]);
                else rotate(x);
    }

    int access(int x)
    {
        int nxt=0;
        for (;x;update(nxt=x),x=par[x])
        {
            splay(x,0);
            if (son[x][1]) par[son[x][1]]=x,fa[son[x][1]]=0;
            if (nxt) par[nxt]=0,fa[nxt]=x;
            son[x][1]=nxt;
        }
        return nxt;
    }

    void makeroot(int x){R(access(x));}

    void modify(int x){makeroot(1),access(x),splay(x,0),ADD(x,1);}

    LL query(int x){return makeroot(1),access(x),splay(x,0),sum[x];}
};

void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}

void dfs(int x)
{
    for (int i=last[x],y;i;i=nxt[i])
        if ((y=tov[i])!=fa[x]) fa[y]=x,dfs(y);
    link_cut_tree::val[x]=x-fa[x],link_cut_tree::update(x),link_cut_tree::par[x]=fa[x];
}

int main()
{
    freopen("bwt.in","r",stdin),freopen("bwt.out","w",stdout);
    n=read(),q=read();
    for (int i=1;i<=n;++i) col[i]=read();
    for (int i=1,x,y;i<n;++i) x=read(),y=read(),insert(x,y),insert(y,x);
    fa[1]=0,dfs(1);
    for (int tp,x;q--;)
    {
        tp=read()-1,x=read();
        if (tp) ans[x]+=(col[x]?1:-1)*link_cut_tree::query(x),col[x]^=1;
        else link_cut_tree::modify(x);
    }
    for (int x=1;x<=n;++x) if (col[x]) ans[x]+=link_cut_tree::query(x);
    for (int x=1;x<=n;++x) write(ans[x]),putchar('\n');
    fclose(stdin),fclose(stdout);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章