分析

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. SqlServer中#和##的区别

    本地临时表的名称以单个数字字符(#)开头,它们仅对当前的用户连接是可见的. 全局临时表的名称以两个数字字符(##)开头,创建后对任何用户都是可见的.

  2. 平面割线平分点(构造)--牛客第三场-- Magic Line

    题意: 给你n个点的坐标,让你给出两个点,这两个点的连线可以平分这些点. 思路: 先按y的大小排序,在按x的小排序,再搞一下就行了.如下图: #include <bits/stdc++.h> ...

  3. php 内存原理

    1.内存结构: 栈区:保存变量名,对CPU,读写速度很快 堆区:保存复杂的数据结构,想对象,数组,复杂的字符串等. 数据段:数据段分为数据段全局区(简单的数据,整型和布尔类型)和数据段静态区(存储静态 ...

  4. 从入门到自闭之Python字典如何使用

    字典: 定义:dict dict = {"key":"value"} -- 键值对 作用:存储大量数据,数据和数据起到关联作用 所有的操作都是通过键来完成 键: ...

  5. Dubbo架构

    原文链接http://dubbo.apache.org 架构图 节点角色说明 节点 角色说明 Provider 暴露服务的服务提供方 Consumer 调用远程服务的服务消费方 Registry 服务 ...

  6. windows下搭建spark+python 开发环境

    有时候我们会在windows 下开发spark程序,测试程序运行情况,再部署到真实服务器中运行. 那么本文介绍如何在windows 环境中搭建简单的基于hadoop 的spark 环境. 我的wind ...

  7. JavaScript冒泡排序法实现排序操作

    var arr = [10,8,6,9,1,7,1,13,5,1,9]; //冒泡排序 function bubbleSort(tmpArr){ for(var i = tmpArr.length-1 ...

  8. 【Activiti】为每一个流程绑定相应的业务对象的2种方法

    方式1: 在保存每一个流程实例时,设置多个流程变量,通过多个流程变量的组合来过滤筛选符合该组合条件的流程实例,以后在需要查询对应业务对象所对应的流程实例时,只需查询包含该流程变量的值的流程实例即可. ...

  9. 第八篇 CSS定位

    CSS定位 CSS除了内外边距控制元素,还有定位,看到“定位”两个字,同学们应该就能清楚,它能够做什么.   在刚学习的时候,我也经常使用定位,来控制元素的位置,但是初学的同学可能会注意不到定位的一些 ...

  10. Nginx默认配置语法

    Nginx默认配置语法 1. 我们进入  /etc/nginx/目录下,打开  nginx.conf文件 2. 我们来解析下 这里面标签和各模块的作用 # 设置nginx服务的系统使用用户 user ...