分析

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. Head First PHP&MySQl第一章代码

    HTML: <!doctype html> <html lang="zh-cn"> <head> <meta charset=" ...

  2. JDK 监控和故障处理工具总结 (转)

    出处:  JDK 监控和故障处理工具总结 JDK 监控和故障处理工具总结 JDK 命令行工具 jps:查看所有 Java 进程 jstat: 监视虚拟机各种运行状态信息 jinfo: 实时地查看和调整 ...

  3. idea 修改pom文件jdk版本回退问题解决

    在Java开发是我们大多都使用集成开发环境,像idea和eclipse用的都比较多,在使用idea maven构建项目时,在修改pom.xml文件时,我们的项目jdk版本都会回退,还得每次去设置中修改 ...

  4. 首个springboot项目总结

    项目说明:采用分布式架构的基于restfulApi的后端微服务api项目. 用到的技术栈:springboot.mybatis.swagger.eureka注册中心.gateway.awss3.doc ...

  5. 查看Linux服务器硬件信息

    一:查看cpu# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数# 查看物理CPU个数cat /proc/cpui ...

  6. Redhat 7修改主机名

    修改主机名: Linux master2 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 G ...

  7. dedecms 列表标签 去斜杠 去两边空格

    首先:将 include/arc.listview.class.php 文件的第53行: $this->Fields['title'] = ereg_replace("[<> ...

  8. deep_learning_LSTM长短期记忆神经网络处理Mnist数据集

    1.RNN(Recurrent Neural Network)循环神经网络模型 详见RNN循环神经网络:https://www.cnblogs.com/pinard/p/6509630.html 2. ...

  9. 从0开始Jmeter接口测试实战

    在之前的文章中给大家介绍过接口测试文档和接口测试用例示例,本文基于Jmeter工具给大家介绍一下如何实现接口测试用例:包括发起Http请求,绕过登陆,验证响应.JMeter是Apache组织开发的基于 ...

  10. VMware Tools按钮变灰色,无法安装的解决方法

    参考博客: https://blog.csdn.net/weixin_30639719/article/details/94846851 https://jingyan.baidu.com/artic ...