CF1172E Nauuo and ODT LCT
自己独立想出来的,超级开心
一开始想的是对于每一个点分别算这个点对答案的贡献.
但是呢,我们发现由于每一条路径的贡献是该路径颜色种类数,而每个颜色可能出现多次,所以这样就特别不好算贡献.
那么,还是上面那句话,由于算的是颜色种类,所以我们可以对每一个颜色种类单独算贡献.
即不以点为单位去算,而是以颜色种类为单位去算.
假设要算颜色为 $r$ 的贡献,那么必须保证每一个路径至少有一个端点在颜色 $r$ 构成的连通块中.
这句话等同于不能出现两个端点都在非 $r$ 连通块的路径,即 $n^2-\sum_{col[i]\neq r}size[i]^2$
对于每一个颜色都这么算就好了 ~
具体的话需要离线+撤销+LCT维护子树信息(就是那个平方和)
然后还要用到那个点权转边权,每次只删除和父亲连边的那个套路 ~
code:
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 600003
#define LL long long
#define lson t[x].ch[0]
#define rson t[x].ch[1]
#define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout)
using namespace std;
LL ans,re[N];
int edges;
int fa[N],hd[N],to[N<<1],nex[N<<1],val[N],size[N],col[N],is[N];
void add(int u,int v)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
struct data
{
int u,v,tim;
data(int u=0,int v=0,int tim=0):u(u),v(v),tim(tim){}
};
vector<data>G[N];
struct node
{
LL sqr;
int ch[2],rev,f,siz,son;
}t[N];
int get(int x)
{
return t[t[x].f].ch[1]==x;
}
int isrt(int x)
{
return !(t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x);
}
void pushup(int x)
{
t[x].siz=t[lson].siz+t[rson].siz+t[x].son+1;
}
void rotate(int x)
{
int old=t[x].f,fold=t[old].f,which=get(x);
if(!isrt(old)) t[fold].ch[t[fold].ch[1]==old]=x;
t[old].ch[which]=t[x].ch[which^1],t[t[old].ch[which]].f=old;
t[x].ch[which^1]=old,t[old].f=x,t[x].f=fold;
pushup(old),pushup(x);
}
void splay(int x)
{
int u=x,fa;
for(;!isrt(u);u=t[u].f);
for(u=t[u].f;(fa=t[x].f)!=u;rotate(x))
{
if(t[fa].f!=u)
{
rotate(get(fa)==get(x)?fa:x);
}
}
}
void Access(int x)
{
for(int y=0;x;y=x,x=t[x].f)
{
splay(x);
if(rson)
{
t[x].son+=t[rson].siz;
t[x].sqr+=(LL)t[rson].siz*t[rson].siz;
}
if(y)
{
t[x].son-=t[y].siz;
t[x].sqr-=(LL)t[y].siz*t[y].siz;
}
rson=y;
pushup(x);
}
}
void link(int x,int y)
{
Access(x),splay(x);
t[y].f=x;
t[x].son+=t[y].siz;
t[x].sqr+=(LL)t[y].siz*t[y].siz;
pushup(x);
}
// x 与 x 的父亲
void cut(int x)
{
Access(x),splay(x);
if(lson)
{
t[lson].f=0;
lson=0;
pushup(x);
}
}
int findroot(int x)
{
Access(x),splay(x);
while(lson) x=lson;
return x;
}
void turn_0(int x)
{
Access(x),splay(x);
int now=t[x].siz;
ans-=t[x].sqr;
if(fa[x]) link(fa[x],x);
int p=findroot(x);
splay(p);
is[x]=0;
p=is[p]?t[p].ch[1]:p;
int ori=t[p].siz;
ans-=(LL)(ori-now)*(ori-now);
ans+=(LL)ori*ori;
}
void turn_1(int x)
{
int p=findroot(x);
splay(p);
p=is[p]?t[p].ch[1]:p;
int ori=t[p].siz;
ans-=(LL)ori*ori;
cut(x);
int now=t[x].siz;
ans+=(LL)(ori-now)*(ori-now);
ans+=(LL)t[x].sqr;
is[x]=1;
}
void dfs(int u,int ff)
{
size[u]=1;
fa[u]=t[u].f=ff;
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs(v,u);
size[u]+=size[v];
t[u].son+=size[v];
t[u].sqr+=(LL)size[v]*size[v];
}
pushup(u);
}
int main()
{
// setIO("input");
int i,j,n,m,mx=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d",&col[i]);
val[i]=col[i];
mx=max(mx,col[i]);
G[val[i]].push_back(data(i,val[i],0));
}
for(i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
for(i=1;i<=m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
mx=max(mx,v);
if(val[u]==v) continue;
G[val[u]].push_back(data(u,v,i)); // val[u]->v
G[v].push_back(data(u,v,i)); // ?->v
val[u]=v;
}
for(i=1;i<=mx;++i)
{
ans=(LL)n*n;
LL pre=0;
for(j=0;j<G[i].size();++j)
{
if(G[i][j].v==i) // 别的变成 i (0->1)
{
turn_1(G[i][j].u);
}
else // i 变成别的 (1->0)
{
turn_0(G[i][j].u);
}
re[G[i][j].tim]-=pre;
re[G[i][j].tim]+=(LL)n*n-ans;
pre=(LL)n*n-ans;
}
for(j=G[i].size()-1;j>=0;--j)
{
if(G[i][j].v==i) // 别的变成 i (0->1)
{
turn_0(G[i][j].u);
}
else // i 变成别的 (1->0)
{
turn_1(G[i][j].u);
}
}
}
printf("%lld\n",re[0]);
for(i=1;i<=m;++i) re[i]+=re[i-1], printf("%lld\n",re[i]);
return 0;
}
CF1172E Nauuo and ODT LCT的更多相关文章
- CF1172E Nauuo and ODT
CF1172E Nauuo and ODT 神仙题orz 要算所有路径的不同颜色之和,多次修改,每次修改后询问. 对每种颜色\(c\)计算多少条路径包含了这个颜色,不好算所以算多少条路径不包含这个颜色 ...
- cf1172E Nauuo and ODT(LCT)
首先可以转化问题,变为对每种颜色分别考虑不含该颜色的简单路径条数.然后把不是当前颜色的点视为白色,是当前颜色的点视为黑色,显然路径数量是每个白色连通块大小的平方和,然后题目变为:黑白两色的树,单点翻转 ...
- Codeforces 1172E Nauuo and ODT [LCT]
Codeforces ZROI那题是这题删掉修改的弱化版--ZROI还我培训费/px 思路 按照套路,我们考虑每种颜色的贡献,然后发现不包含某种颜色的路径条数更容易数,就是删掉该颜色的点后每个连通块大 ...
- [CF1172E]Nauuo and ODT:Link-Cut Tree
分析 lxl大毒瘤. 感谢Ouuan等CNOIER提供了这么好的比赛. 这里只是把官方题解复述一遍,可以直接去看官方题解:点我. 考虑将问题转化为对于每个颜色,求出没有经过这个颜色的节点的路径有多少条 ...
- CF 1172E Nauuo and ODT ——LCT
题目:http://codeforces.com/contest/1172/problem/E LCT好题. 考虑对每个颜色求出 “不是该颜色的点组成的连通块的 siz2 之和” .每个颜色用 LCT ...
- 题解 CF1172E Nauuo and ODT
题目传送门 题目大意 给出一个 \(n\) 个点的树,每个点有颜色,定义 \(\text{dis}(u,v)\) 为两个点之间不同颜色个数,有 \(m\) 次修改,每次将某个点的颜色进行更改,在每次操 ...
- 【CF1172E】Nauuo and ODT(Link-Cut Tree)
[CF1172E]Nauuo and ODT(Link-Cut Tree) 题面 CF 给你一棵树,每个节点有一个颜色. 定义一条路径的权值为路径上不同颜色的数量.求所有有向路径的权值和. 有\(m\ ...
- 【杂题】[CodeForces 1172E] Nauuo and ODT【LCT】【口胡】
Description 给出一棵n个节点的树,每个点有一个1~n的颜色 有m次操作,每次操作修改一个点的颜色 需要在每次操作后回答树上\(n^2\)条路径每条路径经过的颜色种类数和. \(n,m< ...
- 【CodeForces】1172E. Nauuo and ODT
题解 看了一遍题解(以及代码)但是没写代码-- 后来做梦的时候忽然梦到了这道题--意识到我需要补一下-- 这道题就是,对于每种颜色,把没有染成这种颜色的点标成黑点,然后计算每个联通块的平方 然后每个点 ...
随机推荐
- Typora使用技巧系列:(1)
Typora使用技巧(1) 刚刚开了博客怎么说也要学一下markdown语法什么的吧,使用的是编译器是Typora,之后有空会陆续更新的 切换到源代码模:(ctrl + /)临时切换到源代码模式,再按 ...
- POI2015 WYC
也许更好的阅读体验 \(\mathcal{Description}\) 给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种.将所有可能的路径按路径长度排序,请输出第k小的路径的长 ...
- bat命令复制文件
del HDT675.applicationdel setup.exedel publish.htmRD /S /Q "Application Files"xcopy /E \\1 ...
- ADO.net(内置类区别)随记
Ado.net使用流程 SqlConnection->open->SqlCommand(sqlstring,conn)->(ExcuteNonQuery \ExecuteScalar ...
- python pip安装解决方法
一招解决python pip install 安装库失败 PIP是python强大的安装利器,但是我们经常遇到安装库失败的问题,以下本人觉得最有效的解决方法: 1.打开 https://www.l ...
- js数组(五)
一.数组的声明方法一:[构造函数形式声明] var array = new Array(); //[] var array = new Array(10); //数组长度:array.length = ...
- pgrep,pkill
pgrep, pkill - look up or signal processes based on name and other attributes 根据名称和其它属性来查找进程 pgrep: ...
- Xshell连接虚拟机中的Ubuntu
虚拟机中安装好Ubuntu系统后使用cmd测试ping 设置xshell的连接ip 连接 连接失败 安装openssh-server sudo apt install openssh-server 再 ...
- BS开发扫盲篇-前端技术风云录
BS开发扫盲篇-前端技术风云录 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.HTML诞生 HTML(HyperText Markup Language)超文本标记语言 ...
- C++(四十六) — 异常处理机制、标准IO输入输出
1.异常处理机制 一般来说,异常处理就是在程序运行时对异常进行检测和控制.而在C++ 中,使用 try-throw-catch模式进行异常处理的机制. #include<iostream> ...