Solution -「NOI 模拟赛」彩色挂饰
\(\mathcal{Description}\)
给定一个含 \(n\) 个点 \(m\) 条边的简单无向图,设图中最大点双的大小为 \(s\),则保证 \(s\le6\)。你将要用 \(k\) 种颜色为结点染色,其中有些结点需要染成的颜色被确定,其余结点颜色任意。一次染色可以将一块全部无色的连通块染成某种颜色。求最少染色次数。
\(n\le10^5\),\(k\le20\),\(s\le6\)。
\(\mathcal{Solution}\)
提到点双,尝试建出广义圆方树,在其上 DP。
令 \(f(u,c)\) 表示圆点 \(u\) 或方点 \(u\) 的祖先的颜色为 \(c\) 时,染好 \(u\) 子树的最少操作次数。对于不定颜色的圆点 \(u\),显然有
\]
即,在方儿子的位置钦定自己的颜色,自己所在的连通块可以一起合并。
接下来考虑方点 \(u\) 的转移。设 \(u\) 所代表的极大点双为 \(C=\{v_0,v_1,\cdots,v_k\}\),其中 \(v_0\) 为 \(u\) 在圆方树上的父亲。对于每个 \(S\subseteq C\),预处理出 \(\operatorname{con}(S)\) 表示点集 \(S\) 的导出子图是否连通,即
\]
再令 \(g(S,c)\) 表示将 \(C\) 的连通子集 \(S\) 作为一个 \(c\) 颜色连通块时,将 \(v\in S\) 及 \(v\) 的子树们染色的最少操作次数。类似于圆点转移,有
\]
顺带令 \(G(S)=\min\{g(S,c)\}\),在此基础上,令 \(h(S)\) 表示将 \(S\) 分为若干个连通块时,将 \(v\in S\) 及 \(v\) 的子树们染色的最少操作次数,就有
\]
最终,枚举 \(v_0\) 所在连通块,得到转移
\]
复杂度为 \(\mathcal O(n(3^s+k2^s))\)。
\(\mathcal{Code}\)
/* Clearink */
#include <cstdio>
#include <cstring>
#include <unordered_set>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
inline int rint() {
int x = 0, s = getchar();
for ( ; s < '0' || '9' < s; s = getchar() );
for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
return x;
}
inline void chkmin( int& a, const int b ) { b < a && ( a = b ); }
const int MAXN = 1e5, MAXM = 6e5, MAXK = 20, INF = 1000000;
int n, m, K, s, clr[MAXN + 5], vnode;
template<const int NODE, const int EDGE>
struct Graph {
int ecnt, head[NODE], to[EDGE], nxt[EDGE];
inline void operator () ( const int s, const int t ) {
#ifdef RYBY
printf( "%d %d\n", s, t );
#endif
to[++ecnt] = t, nxt[ecnt] = head[s], head[s] = ecnt;
}
};
#define adj( g, u, v ) \
for ( int e = g.head[u], v; v = g.to[e], e; e = g.nxt[e] )
Graph<MAXN + 5, MAXM * 2 + 5> src;
Graph<MAXN * 2 + 5, MAXN * 2 + 5> cac;
std::unordered_set<int> adm[MAXN + 5];
inline void buildCactus( const int u, const int fa ) {
static int dfc, top, dfn[MAXN + 5], low[MAXN + 5], stk[MAXN + 5];
dfn[u] = low[u] = ++dfc, stk[++top] = u;
adj ( src, u, v ) if ( v != fa ) {
if ( !dfn[v] ) {
buildCactus( v, u ), chkmin( low[u], low[v] );
if ( low[v] >= dfn[u] ) {
cac( u, ++vnode );
do cac( vnode, stk[top] ); while ( stk[top--] != v );
}
} else chkmin( low[u], dfn[v] );
}
}
int f[MAXN * 2 + 5][MAXK + 5];
inline void solve( const int u, const int fa ) {
adj ( cac, u, v ) solve( v, u );
if ( u <= n ) {
if ( clr[u] ) {
rep ( i, 1, K ) f[u][i] = INF;
f[u][clr[u]] = 1;
adj ( cac, u, v ) f[u][clr[u]] += f[v][clr[u]] - 1;
} else {
rep ( i, 1, K ) f[u][i] = 1;
adj ( cac, u, v ) rep ( i, 1, K ) f[u][i] += f[v][i] - 1;
}
} else {
static bool con[100];
static int id[MAXN * 2 + 5], bitw[100], vec[10], ads[10];
static int g[100][MAXK + 5], h[100];
if ( !id[0] ) { // once.
memset( id, 0xff, sizeof id );
rep ( i, 2, 1 << 6 ) bitw[i] = bitw[i >> 1] + 1;
}
id[0] = vec[9] = 1, id[fa] = 0, vec[0] = fa;
adj ( cac, u, v ) id[v] = id[0]++, vec[vec[9]++] = v;
rep ( i, 0, vec[9] - 1 ) {
ads[i] = 0;
rep ( j, 0, vec[9] - 1 ) {
ads[i] |= int( i == j || adm[vec[i]].count( vec[j] ) ) << j;
}
}
rep ( S, 1, ( 1 << vec[9] ) - 1 ) {
if ( !S || !( S & ( S - 1 ) ) ) con[S] = true;
else {
con[S] = false;
rep ( v, 0, vec[9] - 1 ) if ( S >> v & 1 ) {
con[S] = con[S ^ ( 1 << v )]
&& ( ads[v] & ( S ^ ( 1 << v ) ) );
if ( con[S] ) break;
}
}
rep ( i, 1, K ) {
if ( !con[S] ) g[S][i] = INF;
else {
g[S][i] = 1;
rep ( j, 1, vec[9] - 1 ) if ( S >> j & 1 ) {
g[S][i] += f[vec[j]][i] - 1;
}
}
}
}
rep ( S, 0, ( 1 << vec[9] ) - 1 ) {
g[S][0] = INF;
rep ( i, 1, K ) {
chkmin( g[S][0], g[S][i] );
}
}
h[0] = 0;
rep ( S, 1, ( 1 << vec[9] ) - 1 ) {
h[S] = INF;
for ( int T = S; T; T = ( T - 1 ) & S ) {
chkmin( h[S], h[S ^ T] + g[T][0] );
}
}
rep ( i, 1, K ) {
f[u][i] = INF;
for ( int S = 1; S < 1 << vec[9]; S += 2 ) {
chkmin( f[u][i], g[S][i] + h[( ( 1 << vec[9] ) - 1 ) ^ S] );
}
}
}
// printf( "node %d:\n", u );
// rep ( i, 1, K ) printf( "%d%c", f[u][i], i < K ? ' ' : '\n' );
}
int main() {
freopen( "colorful.in", "r", stdin );
freopen( "colorful.out", "w", stdout );
n = rint(), m = rint(), K = rint(), s = rint();
rep ( i, 1, n ) clr[i] = rint();
rep ( i, 1, m ) {
int u = rint(), v = rint();
src( u, v ), src( v, u );
adm[u].insert( v ), adm[v].insert( u );
}
#ifdef RYBY
puts( "+++ +++ +++" );
#endif
vnode = n, buildCactus( 1, 0 );
#ifdef RYBY
puts( "--- --- ---" );
#endif
solve( 1, 0 );
int ans = INF;
rep ( i, 1, K ) chkmin( ans, f[1][i] );
printf( "%d\n", ans );
return 0;
}
Solution -「NOI 模拟赛」彩色挂饰的更多相关文章
- Solution -「NOI 模拟赛」出题人
\(\mathcal{Description}\) 给定 \(\{a_n\}\),求一个 \(\{b_{n-1}\}\),使得 \(\forall x\in\{a_n\},\exists i,j\ ...
- 「CSP-S模拟赛」2019第四场
「CSP-S模拟赛」2019第四场 T1 「JOI 2014 Final」JOI 徽章 题目 考场思考(正解) T2 「JOI 2015 Final」分蛋糕 2 题目 考场思考(正解) T3 「CQO ...
- 「NOIP模拟赛」数位和乘积(dp,高精)
统计方案数,要么组合数,要么递推(dp)了. 这是有模拟赛历史以来爆炸最狠的一次 T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0 T3看错题本来简单模拟又 ...
- 「CSP-S模拟赛」2019第三场
目录 T1 「POI2007」山峰和山谷 Ridges and Valleys 题目 考场思路(几近正解) 正解 T2 「JOI 2013 Final」 现代豪宅 题目 考场思路(正解) T3 「SC ...
- 「CSP-S模拟赛」2019第二场
目录 T1 Jam的计数法 题目 考场思路(正解) T2 「TJOI / HEOI2016」排序 题目 考场思路(假正解) 正解 T3 「THUWC 2017」随机二分图 题目 考场思路 正解 这场考 ...
- 「CSP-S模拟赛」2019第一场
目录 T1 小奇取石子 题目 考场思路 正解 T2 「CCO 2017」专业网络 题目 考场思路 题解 T3 「ZJOI2017」线段树 题目 考场思路 正解 这场考试感觉很奇怪. \(T1.T2\) ...
- 「NOIP模拟赛」Round 3
Tag 计数+LIS, 二分+ST表, 计数+记搜 A. 改造二叉树 Description 题面 Solution 如果目标序列非严格递增,或者说目标序列是不下降的,那么答案就是 \(n\) 减去最 ...
- HHHOJ #153. 「NOI模拟 #2」Kotomi
抽代的成分远远大于OI的成分 首先把一个点定为原点,然后我们发现如果我们不旋转此时答案就是所有位置的\(\gcd\) 如果要选择怎么办,我们考虑把我们选定的网格边连同方向和大小看做单位向量\(\vec ...
- HHHOJ #151. 「NOI模拟 #2」Nagisa
计算几何板子题(我才没有拷板子的说--) 众所周知,三角形的重心坐标是\((\frac{x_1+x_2+x_3}{3},\frac{y_1+y_2+y_3}{3})\) 然后我们发现如果我们有一个点集 ...
随机推荐
- ':app@debug/compileClasspath': Could not find any version that matches com.android.support:appcompat-v7:30.+.
ERROR: Unable to resolve dependency for ':app@debug/compileClasspath': Could not find any version th ...
- js获取设备内网ip
可以直接使用,不需要导入其他配置 看代码 1 <script> 2 //获取内网ip 3 var RTCPeerConnection = window.RTCPeerConnection ...
- VM搭建Hadoop环境遇到的问题
原文链接:https://www.toutiao.com/i6482644128860471822/ 一.Slave2中sshd服务一直处于启动失败状态 查看配置文件 c 发现配置文件末尾slave2 ...
- Jenkins Hackfest 用户体验文档报告
Jenkins 技术文档是我们项目的重要组成部分,因为它是正确使用 Jenkins 的关键.好的文档可以指导用户,并鼓励选择好的实现方式.这是用户体验的关键部分.在最近的 Jenkins UI/UX ...
- 关于Jmeter线程数Ramp-Up.循环次数的理解和实验数据
1. 关于线程组参数 线程组:即一个线程组实例里面包括多个串行的请求或动作.一个线程组的从启动到结束的时间取决于你线程中的步骤数量. 线程数:即用户数,在Ramp-up时间内(包括循环),简单把线程数 ...
- C#进阶——记一次USB HID的各种坑(x86,x64,win10,win7)
一.简叙 写工控上位机的搬砖人,难免会遇到USB通讯,在一个项目中,我写的上位机使用USB HID协议和STM32通讯传输数据,从零大概花了几天找例程,找资料,最后是各种搬砖修补,终于出来了一个出版D ...
- 使用redis+lua实现SQL中的select intersect的效果
公众号文章地址 1.需求 业务中需要实现在两个集合中搜索数据,并返回交集. 用SQL的伪代码可以描述如下: select key from set1 where sorted_key between ...
- java-异常-原理异常对象的抛出throw
1 class Demo { 2 public static int method(int[] arr,int index) { 3 4 // System.out.println(arr[index ...
- 由浅入深,66条JavaScript面试知识点
前言 我只想面个CV工程师,面试官偏偏让我挑战造火箭工程师,加上今年这个情况更是前后两男,但再难苟且的生活还要继续,饭碗还是要继续找的.在最近的面试中我一直在总结,每次面试回来也都会复盘,下面是我这几 ...
- Redis入门及环境搭建
一:Redis简介 Redis(Remote Dictionary Server 远程字典服务)是一个开源的(BSD许可的)内存数据结构存储,用作数据库.高速缓存和消息队列代理. Redis提供五大基 ...