Solution -「UR #21」「UOJ #632」挑战最大团
\(\mathcal{Description}\)
Link.
对于简单无向图 \(G=(V,E)\),定义它是“优美”的,当且仅当
\]
给定一个“优美”的简单无向图 \(G\),对于所有 \(i\in[1,n]\),求有多少个 \(S\sube V\),\(|S|=i\) 且 \(S\) 的导出子图是完全图。
\(\mathcal{Solution}\)
直接做铁铁的 NPC,必须从“优美”的性质入手分析。
性质一,\(G\) 的直径不超过 \(2\)。由定义显然。
性质二,记 \(G\) 的补图为 \(\overline{G}\),则 \(G\) 与 \(\overline{G}\) 不同时连通。证明如下:
归纳,当点集 \(|V|=2\) 时,显然成立。若当 \(|V|<n\) 时命题成立,并假设当 \(|V|=n\) 时命题不成立:
取任意 \(u\in V\),由于命题不成立,故在 \(G\) 或是 \(\overline{G}\) 中,\(u\) 都至少有一条连入点集 \(V\setminus\{u\}\) 的边。设点集 \(V\setminus\{u\}\) 的导出子图为 \(G'\),补图为 \(\overline{G'}\),由于 \(|V|\setminus\{u\}=n-1\),由归纳,\(G'\) 与 \(\overline{G'}\) 不同时连通。
不妨设 \(G'\) 不连通,则令其极大连通分量为 \(G_1,G_2,\cdots,G_k~(k>1)\)。由于 \(u\) 在 \(\overline{G}\) 中右边,所以必然存在 \(t\in G_i\),使得 \((u,t)\not\in E\)。接着研究这个 \(G_i\),设其点集为 \(V_i\),并令 \(S=\{v\in V_i~|~(u,v)\in E\},T=V_i\setminus S\)。由于假设有 \(G\) 连通,所以 \(|S|,|T|>0\)。任取 \(v\in S\),因为 \(G_i\) 连通,故存在 \(w\in T\),使得 \((v,w)\in E\),最后,再取任意 \(x\in G_j~(j\not=i)\) 使得 \((u,x)\in E\)。考虑点集 \(\{x,u,v,w\}\):
- \((x,u),(u,v),(v,w)\in E\);
- \((x,v),(u,w),(x,w)\not\in E\)。
故该图不合条件,假设不成立,原命题成立。
利用“不连通”带来的子问题结构,我们分别考虑原问题在原图与补图的体现:
对于原图,假设其由 \(G_1,G_2,\cdots,G_k~(k>1)\) 组成,设 \(F(G)\) 为图 \(G\) 的答案 OGF,显然有
\[F(G)=\sum_{i=1}^kF(G_i)
\]对于补图,假设其由 \(\overline{G_1},\overline{G_2},\cdots,\overline{G_k}~(k>1)\) 组成,设 \(\overline{F}(\overline{G})\) 为图 \(\overline{G}\) 中独立集数量关于其大小的 OGF,就有
\[\overline{F}(\overline{G})=\prod_{i=1}^k\overline{F}(\overline{G_i})
\]而显然 \(F(G)=\overline{F}(\overline{G})\),所以我们确实可以分治求解。
但是,本题的难点在于构造分治结构,即确定当前层是按原图的连通块划分还是补图的连通块划分。一种优秀的策略是:选择 \(G\) 中度数最小的点 \(x\) 和度数最大(即 \(\overline{G}\) 中度数最小)的点 \(y\),以 \(x\) 为源点在 \(G\) 中,\(y\) 为源点在 \(\overline{G}\) 中分别 BFS:先扩展一层,然后交替扩展第二层(最多两层)直到能够判断某点所在连通块 是/不是 整个图。此后,利用不连通的一个点 BFS 得到的标记数组进行连通块划分,递归建图。
考虑复杂度,瓶颈在于第二层搜索,设 \(x,y\) 的度数大小分别是 \(d_x,d_y\),复杂度上界即是 \(\mathcal O(d_xm+d_ym)\),其中 \(m\) 为当前图的点集大小。记被分离的连通块大小为 \(s\),那么这一上界必然不超过 \(\mathcal O(sm)\)。为方便复杂度分析,设留下部分大小为 \(t\),有 \(s\le t\),故 \(sm=s^2+st=\mathcal O(st)\)。考虑分治树所描述的事件:初始时有 \(n\) 个点,每次将剩下的点分为 \(x,y\) 两部分,代价为 \(xy\),再递归处理 \(x\) 和 \(y\)。则树上每两片叶子在 LCA 处贡献复杂度一次,总复杂度 \(\mathcal O(n^2)\)。
\(\mathcal{Code}\)
/* Clearink */
#include <cstdio>
#include <vector>
#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 )
typedef std::pair<int, int> PII;
#define fi first
#define se second
#define getv( c ) ( '0' <= c && c <= '9' ? c ^ '0' : 10 + c - 'A' )
const int MAXN = 8e3, MOD = 998244353;
int n, node;
bool e[MAXN + 5][MAXN + 5], side[MAXN * 2 + 5];
std::vector<int> son[MAXN * 2 + 5];
inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline int imax( const int a, const int b ) { return a < b ? b : a; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int build( const std::vector<PII>& vec, const bool curs ) {
int o = ++node;
if ( vec.size() == 1 ) return side[o] = curs, o;
static int vis[2][MAXN + 5], que[2][MAXN + 5];
int n = int( vec.size() ), s[2] = {};
rep ( i, 1, n - 1 ) {
if ( vec[i].se < vec[s[0]].se ) s[0] = i;
if ( vec[i].se > vec[s[1]].se ) s[1] = i;
}
s[0] = vec[s[0]].fi, s[1] = vec[s[1]].fi;
vis[0][s[0]] = vis[1][s[1]] = true;
int sz[2] = {};
rep ( sd, 0, 1 ) for ( PII u: vec ) {
if ( !vis[sd][u.fi] && e[s[sd]][u.fi] ^ curs ^ sd ) {
vis[sd][que[sd][++sz[sd]] = u.fi] = true;
}
}
int ex[2] = { sz[0], sz[1] };
bool divs;
rep ( i, 0, imax( sz[0], sz[1] ) ) {
if ( i ) rep ( sd, 0, 1 ) if ( i <= sz[sd] ) {
int u = que[sd][i];
for ( PII v: vec ) {
if ( !vis[sd][v.fi] && e[u][v.fi] ^ curs ^ sd ) {
vis[sd][que[sd][++ex[sd]] = v.fi] = true;
}
}
}
if ( i == imin( sz[0], sz[1] ) ) {
if ( ex[0] == n ) divs = curs;
else if ( ex[1] == n ) divs = !curs;
else { divs = curs ^ ( sz[0] < sz[1] ); break; }
}
}
side[o] = divs;
std::vector<PII> sub[2];
if ( divs == curs ) {
for ( PII u: vec ) {
sub[!vis[1][u.fi]].push_back( { u.fi, n - u.se - 1 } );
vis[0][u.fi] = vis[1][u.fi] = false;
}
for ( PII& u: sub[0] ) for ( PII& v: sub[1] ) {
if ( e[u.fi][v.fi] ^ curs ^ 1 ) {
--u.se, --v.se;
}
}
} else {
for ( PII u: vec ) {
sub[!vis[0][u.fi]].push_back( u );
vis[0][u.fi] = vis[1][u.fi] = false;
}
}
int lc = build( sub[0], !divs ), rc = build( sub[1], !divs );
if ( side[rc] == divs ) son[o] = son[rc];
else son[o].push_back( rc );
son[o].push_back( lc );
return o;
}
int f[MAXN * 2 + 5][MAXN + 5], siz[MAXN + 5];
inline void solve( const int u ) {
static int tmp[MAXN + 5];
if ( son[u].empty() ) return void( f[u][0] = f[u][1] = siz[u] = 1 );
f[u][0] = 1;
for ( int v: son[u] ) {
solve( v );
if ( !side[u] ) { // IG merge.
rep ( i, 0, siz[u] ) rep ( j, 0, siz[v] ) {
addeq( tmp[i + j], mul( f[u][i], f[v][j] ) );
}
rep ( i, 0, siz[u] += siz[v] ) f[u][i] = tmp[i], tmp[i] = 0;
} else { // G merge.
rep ( i, 1, siz[u] = imax( siz[u], siz[v] ) ) {
addeq( f[u][i], f[v][i] );
}
}
}
}
int main() {
scanf( "%d", &n );
rep ( i, 1, n - 1 ) {
static char str[MAXN + 5];
scanf( "%s", str );
rep ( j, 0, n - i - 1 ) {
e[i][i + j + 1] = e[i + j + 1][i] =
getv( str[j >> 2] ) >> ( j & 3 ) & 1;
}
}
std::vector<PII> oriv; oriv.resize( n );
rep ( i, 1, n ) {
int d = 0;
rep ( j, 1, n ) d += e[i][j];
oriv[i - 1] = { i, d };
}
int rt = build( oriv, false );
solve( rt );
rep ( i, 1, n ) printf( "%d%c", f[rt][i], i < n ? ' ' : '\n' );
return 0;
}
Solution -「UR #21」「UOJ #632」挑战最大团的更多相关文章
- 「UR#5」怎样更有力气
「UR#5」怎样更有力气 解题思路 考虑没有限制的情况,一定是把操作离线下来,按照边权从小到达做.可以发现,如果没有限制,完全图是多余的,直接拿树边进行合并就可以了.我们要做这么一件事情,把每个点属于 ...
- 「UR#5」怎样跑得更快
「UR#5」怎样跑得更快 膜这个您就会了 下面是复读机mangoyang 我们要求 \[ \sum_{j=1}^n \gcd(i,j)^{c-d} j^d x_j=\frac{b_i}{i^d} \] ...
- 「UR#6」懒癌
「UR#6」懒癌 妈妈我居然看了六个小时题解,快救救乌干达的可怜儿童吧. 接下来开始膜官方题解: 其实就算有上面两个结论也不是很好想到任意复杂度的做法,关键在于要想到一个人是怎么推断自己的狗是不是 ...
- Solution -「CTS 2019」「洛谷 P5404」氪金手游
\(\mathcal{Description}\) Link. 有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...
- Android内存管理(4)*官方教程 含「高效内存的16条策略」 Managing Your App's Memory
Managing Your App's Memory In this document How Android Manages Memory Sharing Memory Allocating and ...
- LOJ #3049. 「十二省联考 2019」字符串问题
LOJ #3049. 「十二省联考 2019」字符串问题 https://loj.ac/problem/3049 题意:给你\(na\)个\(A\)类串,\(nb\)个\(B\)类串,\(m\)组支配 ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇
http://www.4gamer.net/games/216/G021678/20140714079/ 连载第2回的本回, Arc System Works开发的格斗游戏「GUILTY G ...
- SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法
用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助 ...
随机推荐
- vim - 显示不可见字符(:set list)
默认情况下,vim是不会显示space,tabs,newlines,trailing space,wrapped lines等不可见字符的.我们可以使用以下命令打开list选项,来显示非可见字符: : ...
- SQL高级优化(一)之MySQL优化
不同方案效率对比 MySQL各字段默认长度(一字节为8位) 整型: TINYINT 1 字节 SMALLINT 2 个字节 MEDIUMINT 3 个字节 INT 4 个字节 INTEGER 4 个字 ...
- Jupyter常用配置
一 安装 pip install --upgrade jupyterthemes 二 设置主题 #查看主题列表 jt -l #设置主题并打开工具栏 jt -t chesterish -T 三 设置列 ...
- 解读与部署(三):基于 Kubernetes 的微服务部署即代码
在基于 Kubernetes 的基础设施即代码一文中,我概要地介绍了基于 Kubernetes 的 .NET Core 微服务和 CI/CD 动手实践工作坊使用的基础设施是如何使用代码描述的,以及它的 ...
- echarts-gl初体验:使用echarts-gl实现3D地球
首先我们要下载引入echarts.js和echarts-gl.js 有需要的自己拿资源哈 链接:https://pan.baidu.com/s/1J7U79ey-2ZN4pjb7RTarjg 提取码: ...
- 使用.NET 6开发TodoList应用(31)——实现基于Github Actions和ACI的CI/CD
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求和目标 在这个系列的最后一节中,我们将使用GitHub Actions将TodoList应用部署到Azure Container ...
- 读 Linux 像读小说「GitHub 热点速览 v.22.03」
本周特推选取了一个画风有点意思的 Linux 代码带读项目 flash-linux0.11-talk,希望有趣的文风能带你读完 Linux 代码.当然画风可以增加阅读体验,彩色标记也是一种学习方法-- ...
- 多线程-创建线程第二种方式-实现Runnable接口-细节和好处
1 package multithread2; 2 3 /* 4 * 创建线程的第一种方法:继承Thread类 5 * 6 * 创建线程的第二种方式:实现Runnable接口 7 * 8 * 1,定义 ...
- 函数的参数python教程
一:函数 什么是函数? 函数就类似于工具 提前定义之后可以反复使用 代码冗余 结构清晰 修改繁杂等问题 二:函数的语法结构 def 函数名(参数1,参数2) '''函数注释''' 函数体代码 retu ...
- 利用 Python 进行数据分析(Python 数据分析)· 第 2 版
译者:SeanCheney 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 机器学习交流群 629470233 ApacheCN 学习资源 Sklearn 与 ...