[FJOI2020]染色图的联通性问题 题解
FJOI2020 D1T2
题目大意
给出一个由 $n$ 个点 $m$ 条边构成的染色无向图,求删去每一个点及与其相连的边后图中不连通的同色点对数量。$n,m\leq 10^5$。
思路分析
可以想到先统计原图的答案,然后对删去每个点后的多出的答案进行计算,输出时加上即可。
原图的答案很容易统计,遍历一遍同时计算即可。
如何统计删去每个点后多出的答案?模拟过后很容易发现,多出的答案就是删去这个点后断开的连通块之间形成的同色点对,只需要知道断开后每个连通块的各色点的数量即可。暴力统计显然复杂度太高,连最低档的分都拿不到。毒瘤FJOI
可以想到用 tarjan 找删去后的各个连通块,统计用启发式合并或线段树合并。线段树合并实现简单但是空间较大,但是还是有神犇卡过去了。这里用的是线段树合并。
合并的时候怎么计算呢?
设当前已合并的连通块该颜色的点数和为 $x$ ,原连通块该颜色的点数为 $y$ ,那么遍历一个新的连通块时,设该连通块该颜色的点数为 $z$ ,则将该连通块合并后与其它部分断开后的贡献为为 $z*(y-x-z)+x*(y-x-z)=(x+z)(y-x-z)$ 。注意,若该连通块与原连通块之间会被断掉产生多出的答案,则需要加上 $x*z$ 。
注意,上面的原连通块断开后即为当前节点的父节点所在的连通块。
这样这道题就很好解了。对于原图中的每个连通块:
1. 先遍历一遍,计算出连通块中每个颜色的点数
2. 跑一遍 tarjan ,同时合并数据,计算断开连通块中的每个点后多出的答案
3. 再遍历一遍,计算连通块与原图的其它连通块贡献的答案,然后将当前连通块的数据清空
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=5e5+100;
struct Seg
{
int lson,rson;
ll val,sumv;
#define lson(i) t[i].lson
#define rson(i) t[i].rson
#define val(i) t[i].val
#define sumv(i) t[i].sumv
}t[N*21];
int n,m,tot,cnt,D;
ll now,sum;
int head[N],ver[2*N],Next[2*N];
int rt[N],c[N],nowc[N],sumc[N],dfn[N],low[N];
ll ans[N];
bool vp[N],vq[N];
void add(int x,int y)
{
ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
ver[++tot]=x,Next[tot]=head[y],head[y]=tot;
}
void change(int &p,int l,int r,int k)
{
if(!p)
p=++cnt;
if(l==r)
{
val(p)++,sumv(p)+=nowc[l]-1;
return ;
}
int mid=l+r>>1;
if(k<=mid)
change(lson(p),l,mid,k);
else
change(rson(p),mid+1,r,k);
sumv(p)=sumv(lson(p))+sumv(rson(p));
}
int merge(int x,int y,int l,int r)
{
if(!x)
return y;
if(!y)
return x;
if(l==r)
{
sumv(x)=(val(x)+val(y))*(nowc[l]-val(x)-val(y));//断开后的答案
now+=val(x)*val(y);//当前合并的两个连通块断开的贡献
val(x)+=val(y);
return x;
}
int mid=l+r>>1;
lson(x)=merge(lson(x),lson(y),l,mid);
rson(x)=merge(rson(x),rson(y),mid+1,r);
sumv(x)=sumv(lson(x))+sumv(rson(x));
return x;
}
void pre(int x)
{
vp[x]=1;nowc[c[x]]++;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(!vp[y])
pre(y);
}
}//先遍历一遍,计算出连通块中每个颜色的点数
void tarjan(int x)
{
int nowr=0;//临时根
low[x]=dfn[x]=++cnt;
change(rt[x],1,D,c[x]);
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])//断开会使连通块断开,类似割点
{
now=0;
rt[x]=merge(rt[x],rt[y],1,D);
ans[x]+=now;//多出的答案
}
else
nowr=merge(nowr,rt[y],1,D);//不会断开,合并到临时根上,避免多统计答案
}
else
low[x]=min(low[x],dfn[y]);
}
ans[x]+=sumv(rt[x]);
rt[x]=merge(rt[x],nowr,1,D);//合并临时根
}//跑一遍 tarjan ,同时合并数据,计算断开连通块中的每个点后多出的答案
void query(int x)
{
vq[x]=1;
sum+=nowc[c[x]]*sumc[c[x]];//计算当前连通块与其它连通块的贡献
sumc[c[x]]+=nowc[c[x]],nowc[c[x]]=0;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
if(!vq[y])
query(y);
}
}//再遍历一遍,计算当前连通块与原图的其它连通块贡献的答案,然后将当前连通块的数据清空
int main()
{
//freopen("pair.in","r",stdin);
//freopen("pair.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]),D=max(D,c[i]);
for(int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
pre(i),tarjan(i),query(i);
for(int i=1;i<=n;i++)
printf("%lld\n",sum+ans[i]);
return 0;
}
[FJOI2020]染色图的联通性问题 题解的更多相关文章
- tarjan算法,一个关于 图的联通性的神奇算法
一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连通(strongly ...
- FJOI2020 游记
Day -1 啥都不会,药丸 看了看统考题,好难,爆零的节奏 文化课OI双爆炸 尽力吧 Day 0 花三个多小时才到考场 福州真的好热 签到 在小礼堂待了一会,顺便给手机充了电 四点试机,今年用了新系 ...
- 洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈)
洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1311990 原题地址:洛谷P1155 双栈排序 ...
- 题解 P1682 【过家家】
P1682 过家家 题目描述 有2n个小学生来玩过家家游戏,其中有n个男生,编号为1到n,另外n个女生,编号也是1到n.每一个女生可以先选择一个和她不吵嘴的男生来玩,除此之外,如果编号为X的女生的朋友 ...
- PAT甲题题解-1126. Eulerian Path (25)-欧拉回路+并查集判断图的连通性
题目已经告诉如何判断欧拉回路了,剩下的有一点要注意,可能图本身并不连通. 所以这里用并查集来判断图的联通性. #include <iostream> #include <cstdio ...
- bzoj2958: 序列染色(DP)
2958: 序列染色 题目:传送门 题解: 大难题啊(还是我太菜了) %一发大佬QTT 代码: #include<cstdio> #include<cstring> #incl ...
- [专题总结]矩阵树定理Matrix_Tree及题目&题解
专题做完了还是要说两句留下什么东西的. 矩阵树定理通俗点讲就是: 建立矩阵A[i][j]=edge(i,j),(i!=j).即矩阵这一项的系数是两点间直接相连的边数. 而A[i][i]=deg(i). ...
- tarjan算法讲解。
tarjan算法讲解. 全网最详细tarjan算法讲解,我不敢说别的.反正其他tarjan算法讲解,我看了半天才看懂.我写的这个,读完一遍,发现原来tarjan这么简单! tarjan算法,一个关 ...
- Tarjan的缩点&&割点概述
What is Tarjan? Tarjan,是一种用来解决图的联通性的一种有效途径,它的一般俗称叫做:缩点.我们首先来设想一下: 如果我们有一个图,其中A,B,C构成一个环,那么我们在某种条件下,如 ...
随机推荐
- three.js之初探骨骼动画
今后的几篇郭先生主要说说three.js骨骼动画.three.js骨骼动画十分有意思,但是对于初学者来说,学起来要稍微困难一些,官方文档比较少,网上除了用圆柱体的例子就是引用外部模型的,想要熟练使用骨 ...
- Python 字典(Dictionary) fromkeys()方法
描述 Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值.高佣联盟 www.cgewang.com 语法 from ...
- Android Studio中如何使用自定义的framework库
在安卓app开发中,通常不会遇到需要使用自定义framework库的情况,使用的都是标准的内核库.但也有例外,比如针对定制化的ROM,ROM厂商可能在ROM中对安卓源码做过修改,对应用层app暴露出与 ...
- number类型转date类型
遇到用数字记录日期时,进行查询转换. create or replace function num_to_date(s in number) return dateisbegin return to_ ...
- python机器学习经典实例PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书
点击获取提取码:caji 在如今这个处处以数据驱动的世界中,机器学习正变得越来越大众化.它已经被广泛地应用于不同领域,如搜索引擎.机器人.无人驾驶汽车等.Python机器学习经典实例首先通过实用的案例 ...
- 可笑,你竟然不知道 Java 如何生成 UUID
先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章. ...
- mysql 常用的数据类型
数字类: 整数 tinyint smallint mediumint int bigint 浮点类:float double 定点类:decimal(M,D) 日期 ...
- SpringBoot中使用AOP打印接口日志的方法(转载)
前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在 ...
- “随手记”开发记录day11
今天,我们团队针对每个团队对我们项目的意见或建议进行了探讨,决定了接下来需要改进的地方: (1)背景图片和颜色搭配: (2)增加修改功能: (3)对功能进行更明显划分,让使用者能够更方便的使用.
- Android Studio简单的登陆界面
在app->src->main->java里面找到MainActivity.java,将鼠标放到activity-main上按住Ctrl后单击跳转到activity-main.xml ...