分析

lxl大毒瘤。

感谢Ouuan等CNOIER提供了这么好的比赛。

这里只是把官方题解复述一遍,可以直接去看官方题解:点我

考虑将问题转化为对于每个颜色,求出没有经过这个颜色的节点的路径有多少条,这问题的答案是:

\[\sum_{i=1}^{n}(n^2-\sum_{G'}siz_{G'}^2)
\]

其中\(G'\)是所有不包含颜色为\(i\)的节点的极大连通块。

视颜色为\(i\)的节点为白点,其余为黑点,那么我们现在要做的就是维护一个数据结构,支持:

  1. 修改一个节点的颜色。

  2. 询问所有黑点的极大连通块的大小的平方和。

考虑使用LCT,如果每个黑点向父亲连边的话,那么真实的黑点的极大连通块就是每个连通块去掉根节点后得到的连通块,这样我们可以使用LCT维护子树信息的技巧维护。

注意\(link(x)\)和\(cut(x)\)函数的实现。(这里参考了标程)

时间复杂度为\(O((n+m) \log n)\)。

代码

#include <bits/stdc++.h>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
#define sqr(a) ((a)*(a))
typedef long long LL;
typedef std::pair<int,int> pii; using std::cerr;
using std::endl; inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int MAXN=400005; int n,m,ecnt,head[MAXN];
int c[MAXN],fa[MAXN];
LL ans[MAXN],cur;
std::vector<pii> vec[MAXN]; struct Edge{
int to,nxt;
}e[MAXN<<1]; inline void add_edge(int bg,int ed){
++ecnt;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
} struct lct{
int fa,ch[2];
int siz;
int isiz;
LL ich2;
bool tag;
}a[MAXN]; #define lc a[x].ch[0]
#define rc a[x].ch[1] inline bool isroot(int x){
return a[a[x].fa].ch[0]!=x&&a[a[x].fa].ch[1]!=x;
} inline void pushr(int x){
std::swap(lc,rc);
a[x].tag^=1;
} inline void pushdown(int x){
if(!a[x].tag)return;
if(lc)pushr(lc);
if(rc)pushr(rc);
a[x].tag=false;
} inline void pushup(int x){
a[x].siz=a[lc].siz+a[rc].siz+1+a[x].isiz;
} inline void rotate(int x){
int y=a[x].fa,z=a[y].fa,d=a[y].ch[1]==x,w=a[x].ch[d^1];
if(!isroot(y))a[z].ch[a[z].ch[1]==y]=x;
a[x].ch[d^1]=y;
a[y].ch[d]=w;
if(w)a[w].fa=y;
a[y].fa=x;
a[x].fa=z;
pushup(y);
} int top,sta[MAXN]; inline void splay(int x){
int y=x,z=0;
sta[top=1]=y;
while(!isroot(y))sta[++top]=y=a[y].fa;
while(top)pushdown(sta[top--]);
while(!isroot(x)){
y=a[x].fa,z=a[y].fa;
if(!isroot(y)){
if((a[z].ch[0]==y)==(a[y].ch[0]==x))rotate(y);
else rotate(x);
}
rotate(x);
}
pushup(x);
} inline void access(int x){
for(int y=0;x;x=a[y=x].fa){
splay(x);
a[x].isiz+=a[rc].siz;
a[x].ich2+=sqr(1ll*a[rc].siz);
rc=y;
a[x].isiz-=a[rc].siz;
a[x].ich2-=sqr(1ll*a[rc].siz);
pushup(x);
}
} inline void makeroot(int x){
access(x);
splay(x);
pushr(x);
} inline int findroot(int x){
access(x);
splay(x);
while(pushdown(x),lc)x=lc;
splay(x);
return x;
} inline void split(int x,int y){
makeroot(x);
access(y);
splay(y);
} inline void link(int x,int y){
split(x,y);
a[x].fa=y;
a[y].isiz+=a[x].siz;
a[y].ich2+=sqr(1ll*a[x].siz);
pushup(y);
} inline void cut(int x,int y){
split(x,y);
a[y].ch[0]=0;
a[x].fa=0;
pushup(y);
} inline void link(int x){
int y=fa[x];
access(x);
splay(x);
cur-=a[x].ich2+sqr(1ll*a[rc].siz);
int z=findroot(y);
access(y);
splay(z);
cur-=sqr(1ll*a[a[z].ch[1]].siz);
splay(y);
a[x].fa=y;
a[y].isiz+=a[x].siz;
a[y].ich2+=sqr(1ll*a[x].siz);
pushup(y);
access(x);
splay(z);
cur+=sqr(1ll*a[a[z].ch[1]].siz);
} inline void cut(int x){
int y=fa[x];
access(x);
cur+=a[x].ich2;
int z=findroot(y);
access(x);
splay(z);
cur-=sqr(1ll*a[a[z].ch[1]].siz);
splay(x);
a[lc].fa=0;
lc=0;
pushup(x);
access(y);
splay(z);
cur+=sqr(1ll*a[a[z].ch[1]].siz);
} #undef lc
#undef rc void dfs(int x,int pre){
fa[x]=pre;
trav(i,x){
int ver=e[i].to;
if(ver==pre)continue;
dfs(ver,x);
}
a[x].fa=pre;
a[pre].isiz+=a[x].siz;
a[pre].ich2+=sqr(1ll*a[x].siz);
pushup(pre);
} int main(){
n=read(),m=read();
rin(i,1,n){
c[i]=read();
vec[c[i]].pb(mkpr(i,0));
}
rin(i,2,n){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
rin(i,1,n+1)a[i].siz=1;
dfs(1,n+1);
rin(i,1,m){
int x=read(),y=read();
if(c[x]==y)continue;
vec[c[x]].pb(mkpr(-x,i));
c[x]=y;
vec[c[x]].pb(mkpr(x,i));
}
cur=sqr(1ll*n);
rin(i,1,n){
rin(j,0,Size(vec[i])-1){
int x=vec[i][j].fi;
if(x>0)cut(x);
else link(-x);
ans[vec[i][j].se]+=sqr(1ll*n)-cur;
ans[j==Size(vec[i])-1?m+1:vec[i][j+1].se]-=sqr(1ll*n)-cur;
}
irin(j,Size(vec[i])-1,0){
int x=vec[i][j].fi;
if(x>0)link(x);
else cut(-x);
}
}
rin(i,1,m)ans[i]+=ans[i-1];
rin(i,0,m)printf("%lld\n",ans[i]);
return 0;
}

[CF1172E]Nauuo and ODT:Link-Cut Tree的更多相关文章

  1. 学习笔记:Link Cut Tree

    模板题 原理 类似树链剖分对重儿子/长儿子剖分,Link Cut Tree 也做的是类似的链剖分. 每个节点选出 \(0 / 1\) 个儿子作为实儿子,剩下是虚儿子.对应的边是实边/虚边,虚实时可以进 ...

  2. link cut tree 入门

    鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...

  3. CF1172E Nauuo and ODT

    CF1172E Nauuo and ODT 神仙题orz 要算所有路径的不同颜色之和,多次修改,每次修改后询问. 对每种颜色\(c\)计算多少条路径包含了这个颜色,不好算所以算多少条路径不包含这个颜色 ...

  4. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  5. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  6. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  7. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

  8. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  9. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

随机推荐

  1. 从入门到自闭之Python编码

    ascii码: 支持英文,数字,符号 1字节 不支持中文 gbk(国标) 支持英文,数字,符号 1字节 支持中文 2字节 unicode(万国码): 支持英文,数字,符号 4字节 支持欧洲 4字节 支 ...

  2. C# 面向对象7 命名空间

    命名空间 **namespace(命名空间),用于解决类重名问题,可以看作"类的文件夹" **如果代码和被使用的类在一个namespace则不需要using **在不同命名空间下的 ...

  3. 多Y轴示例

    //多Y轴示例 <template> <div id="main" :style="{width:'1000px',height:'500px' }&q ...

  4. appium 自动化测试框架详读(一)

    appium框架使用的过程记录,开始使用markdown来语法来编写,不知道博客园是否会支持markdown语法 ***1. appium原理* appium启动时,创建一个http://127.0. ...

  5. 点击切换JS

    $(function(){ var tabnav = $("#tab-nav ul li"); tabnav.click(function(){ $(this).addClass( ...

  6. symfony3 使用命令行工具生成Entity实体所踩的坑

    1.把配置文件汇总连接邮箱的配置信息注释掉了,在创建Entity时php bin/console doctrine:generate:entity报错 2. 错误原因是实体文件映射到数据库中的字段时候 ...

  7. 搭建内部NuGet服务

    简介 NuGet相当于Python中的pip,nodejs中的npm,用来管理.net/.net core的程序集版本,也叫包管理器.在框架化.模块化开发中使用nuget服务必不可少,尤其是在abp开 ...

  8. Function HDU - 6546 (数学,贪心)

    wls 有 n 个二次函数 Fi(x) = aix2 + bix + ci (1 ≤ i ≤ n). 现在他想在∑ni=1xi = m 且 x 为正整数的条件下求∑ni=1Fi(xi)的最小值. 请求 ...

  9. 使用TortoiseGit,设置ssh方式连接git仓库。

    开始设置之前的准备:建立项目文件夹,初始化git仓库(右键 git  init),右键打开 git bash ,git pull “仓库地址”, 把网站上的仓库代码拉取下来. TortoiseGit使 ...

  10. 宝塔linux面板命令大全 - 宝塔面板

    安装宝塔 Centos安装脚本 yum install -y wget && wget -O install.sh http://download.bt.cn/install/inst ...