【CF1172E】Nauuo and ODT(Link-Cut Tree)

题面

CF
给你一棵树,每个节点有一个颜色。
定义一条路径的权值为路径上不同颜色的数量。求所有有向路径的权值和。
\(m\)次单点颜色修改操作,每次修改之后输出答案。

题解

如果只有黑白两色,我们要求白色的贡献,那么我们可以把所有白色节点删去,那么答案就是每个黑色连通块的\(size\)平方和。考虑怎么动态维护这个东西。
要做的是,一开始我们的所有节点都是黑点,然后有若干次颜色取反操作,每次求黑色连通块的\(size^2\)的和。
然后拿\(LCT\)维护这个东西,在修改父子关系的时候修改答案。
因为维护的是子树和,所以要维护虚子树信息。
为了防止根节点被染黑不会发生父子关系变化,所以给根节点再额外加一个父亲就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 400400
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
namespace LCT
{
#define ls (t[x].ch[0])
#define rs (t[x].ch[1])
    ll Num;
    struct Node{int ff,ch[2],sz,vsz;ll ssz;ll Val(){return 1ll*sz*sz;}}t[MAX];
    bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
    void pushup(int x){t[x].sz=t[ls].sz+t[rs].sz+t[x].vsz+1;}
    void rotate(int x)
    {
        int y=t[x].ff,z=t[y].ff;
        int k=t[y].ch[1]==x;
        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
        t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
        t[x].ch[k^1]=y;t[y].ff=x;
        pushup(y);pushup(x);
    }
    void Splay(int x)
    {
        while(!isroot(x))
        {
            int y=t[x].ff,z=t[y].ff;
            if(!isroot(y))
                (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
            rotate(x);
        }
    }
    void access(int x)
    {
        for(int y=0;x;y=x,x=t[x].ff)
        {
            Splay(x);
            t[x].vsz-=t[y].sz;
            t[x].vsz+=t[rs].sz;
            t[x].ssz-=t[y].Val();
            t[x].ssz+=t[rs].Val();
            rs=y;pushup(x);
        }
    }
    int findroot(int x){access(x);Splay(x);while(ls)x=ls;Splay(x);return x;}
    void link(int x,int y)
    {
        Splay(x);
        Num-=t[x].ssz+t[rs].Val();
        int z=findroot(y);
        access(x);Splay(z);
        Num-=t[t[z].ch[1]].Val();
        t[x].ff=y;Splay(y);
        t[y].vsz+=t[x].sz;
        t[y].ssz+=t[x].Val();
        pushup(y);access(x);
        Splay(z);
        Num+=t[t[z].ch[1]].Val();
    }
    void cut(int x,int y)
    {
        access(x);Num+=t[x].ssz;
        int z=findroot(y);
        access(x);Splay(z);
        Num-=t[t[z].ch[1]].Val();
        Splay(x);
        t[x].ch[0]=t[t[x].ch[0]].ff=0;
        pushup(x);Splay(z);
        Num+=t[t[z].ch[1]].Val();
    }
}
using namespace LCT;
vector<int> E[MAX];
vector<pair<int,int> >V[MAX];
int n,m,c[MAX],fa[MAX],col[MAX];ll Ans[MAX];
void dfs(int u,int ff){fa[u]=ff;for(int v:E[u])if(v!=ff)dfs(v,u);}
int main()
{
    freopen("a.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;++i)c[i]=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        E[u].push_back(v);
        E[v].push_back(u);
    }
    for(int i=1;i<=n;++i)V[c[i]].push_back(make_pair(0,i));
    for(int i=1;i<=m;++i)
    {
        int u=read(),v=read();
        V[c[u]].push_back(make_pair(i,u));
        c[u]=v;
        V[c[u]].push_back(make_pair(i,u));
    }
    dfs(1,n+1);
    for(int i=1;i<=n+1;++i)pushup(i);
    for(int i=1;i<=n;++i)link(i,fa[i]);
    for(int i=1;i<=n;++i)
    {
        ll lst=0;
        for(auto a:V[i])
        {
            int u=a.second,t=a.first;
            col[u]?link(u,fa[u]):cut(u,fa[u]);
            col[u]^=1;
            Ans[t]+=1ll*n*n-Num-lst;
            lst=1ll*n*n-Num;
        }
        for(auto a:V[i])
        {
            int u=a.second;
            if(col[u])link(u,fa[u]),col[u]^=1;
        }
    }
    for(int i=1;i<=m;++i)Ans[i]+=Ans[i-1];
    for(int i=0;i<=m;++i)printf("%lld\n",Ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/11170635.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值