\(\mathcal{Description}\)

  Link.

  给定一个 \(n\) 阶完全图,边权为 \(1\sim\frac{n(n-1)}2\) 的排列。称一种将点集划分为 \(k\) 组的方案合法,当且仅当对于每个组,与其他组相连的边均大于组内的边。对于 \(k\in[1,n]\),求合法的划分方案数,对 \(998244353\) 取模。

  \(n\le1500\)。

\(\mathcal{Solution}\)

  按边权从小到大加入边,发现原图中某个团可以成为一组,当且仅当存在加边过程中的某个时刻,该团 \((a)\) 已存在(\(m\) 个点间已有 \(\frac{m(m-1)}2\) 条边),\((b)\) 不与其他任何结点邻接。考虑加边过程中维护一个并查集和链表,对于边 \((u,v)\),令其并查集的根为 \(x,y\),若 \(x=y\) 则跳过;否则把 \(y\) 的链表接在 \(x\) 的一端,最后 \(y\leftarrow x\)。不难发现一个团满足 \((b)\) 等价于它的结点是链表上连续的一段区间。

  接下来判断条件 \((a)\),重新加一次边,并查集维护连通块内边数和链表上最左右端的位置,一次合并发现成团就连一条边 \((l,r)\),表示区间 \([l,r]\) 内的结点独立成组合法。注意到合法团的点集要不不交,要不包含,所以至多有 \(2n-1\) 个合法团(此时合法团的包含关系构成完全二叉树)。

  最后就 DP 了,\(f(i,j)\) 表示前 \(i\) 个位置分成 \(j\) 段的方案数,利用判断 \((a)\) 时连出的边转移即可。总复杂度 \(\mathcal O(n^2)\)。

\(\mathcal{Code}\)

#include <cstdio>
#include <vector>
#include <assert.h> inline int rint () {
int x = 0; char 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 wint ( const int x ) {
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
} const int MAXN = 1500, MAXE = MAXN * ( MAXN - 1 ) >> 1, MOD = 998244353;
int n, e, fa[MAXN + 5], fir[MAXN + 5], las[MAXN + 5], nxt[MAXN + 5];
int siz[MAXN + 5], ecnt[MAXN + 5], id[MAXN + 5], f[MAXN + 5][MAXN + 5];
bool nbeg[MAXN + 5];
std::vector<int> trans[MAXN + 5];
std::pair<int, int> ref[MAXE + 5]; inline void addeq ( int& a, const int b ) {
( a += b ) < MOD ? 0 : a -= MOD;
} inline int find ( const int x ) {
return x ^ fa[x] ? fa[x] = find ( fa[x] ) : x;
} inline void kLink ( int x, int y ) {
if ( ( x = find ( x ) ) ^ ( y = find ( y ) ) ) {
fa[y] = x, fir[x] = fir[y], siz[x] += siz[y], ecnt[x] += ecnt[y];
}
if ( ++ ecnt[x] == siz[x] * ( siz[x] - 1 ) >> 1 ) {
trans[las[x]].push_back ( fir[x] );
}
} int main () {
n = rint (), e = n * ( n - 1 ) >> 1;
for ( int i = 1; i <= n; ++ i ) {
fa[i] = fir[i] = las[i] = i;
for ( int j = 1; j <= n; ++ j ) {
int id = rint ();
if ( i < j ) ref[id] = { i, j };
}
}
for ( int i = 1; i <= e; ++ i ) {
int u = find ( ref[i].first ), v = find ( ref[i].second );
if ( u == v ) continue;
nxt[las[u]] = fir[v], las[u] = las[v];
las[v] = fir[v] = -1, fa[v] = u;
}
int cnt = 0;
for ( int i = 1; i <= n; ++ i ) nbeg[nxt[i]] = true;
for ( int i = 1; i <= n; ++ i ) if ( !nbeg[i] ) id[cnt = 1] = i;
assert ( cnt );
for ( int p = id[1]; cnt ++ < n; ) id[cnt] = p = nxt[p];
for ( int i = 1; i <= n; ++ i ) {
fa[i] = fir[id[i]] = las[id[i]] = i;
ecnt[i] = 0, siz[i] = 1;
trans[i].push_back ( i );
}
for ( int i = 1; i <= e; ++ i ) kLink ( ref[i].first, ref[i].second );
f[0][0] = 1;
for ( int i = 0; i < n; ++ i ) {
for ( int j = 0, cur; j <= i; ++ j ) {
if ( !( cur = f[i][j] ) ) continue;
for ( int k: trans[i + 1] ) {
addeq ( f[k][j + 1], cur );
}
}
}
for ( int i = 1; i <= n; ++ i ) {
wint ( f[n][i] );
putchar ( i ^ n ? ' ' : '\n' );
}
return 0;
}

\(\mathcal{Details}\)

  还有一种转化成 Kruskal 重构树上 DP 的方法可以去 % 一下 @crashed 的博客

  这个算法居然比她的慢几倍 qwq。

Solution -「CF 1480G」Clusterization Counting的更多相关文章

  1. Solution -「CF 1342E」Placing Rooks

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的国际象棋棋盘上摆 \(n\) 个车,求满足: 所有格子都可以被攻击到. 恰好存在 \(k\ ...

  2. Solution -「CF 1622F」Quadratic Set

    \(\mathscr{Description}\)   Link.   求 \(S\subseteq\{1,2,\dots,n\}\),使得 \(\prod_{i\in S}i\) 是完全平方数,并最 ...

  3. Solution -「CF 923F」Public Service

    \(\mathscr{Description}\)   Link.   给定两棵含 \(n\) 个结点的树 \(T_1=(V_1,E_1),T_2=(V_2,E_2)\),求一个双射 \(\varph ...

  4. Solution -「CF 923E」Perpetual Subtraction

    \(\mathcal{Description}\)   Link.   有一个整数 \(x\in[0,n]\),初始时以 \(p_i\) 的概率取值 \(i\).进行 \(m\) 轮变换,每次均匀随机 ...

  5. Solution -「CF 1586F」Defender of Childhood Dreams

    \(\mathcal{Description}\)   Link.   定义有向图 \(G=(V,E)\),\(|V|=n\),\(\lang u,v\rang \in E \Leftrightarr ...

  6. Solution -「CF 1237E」Balanced Binary Search Trees

    \(\mathcal{Description}\)   Link.   定义棵点权为 \(1\sim n\) 的二叉搜索树 \(T\) 是 好树,当且仅当: 除去最深的所有叶子后,\(T\) 是满的: ...

  7. Solution -「CF 623E」Transforming Sequence

    题目 题意简述   link.   有一个 \(n\) 个元素的集合,你需要进行 \(m\) 次操作.每次操作选择集合的一个非空子集,要求该集合不是已选集合的并的子集.求操作的方案数,对 \(10^9 ...

  8. Solution -「CF 1023F」Mobile Phone Network

    \(\mathcal{Description}\)   Link.   有一个 \(n\) 个结点的图,并给定 \(m_1\) 条无向带权黑边,\(m_2\) 条无向无权白边.你需要为每条白边指定边权 ...

  9. Solution -「CF 599E」Sandy and Nuts

    \(\mathcal{Description}\)   Link.   指定一棵大小为 \(n\),以 \(1\) 为根的有根树的 \(m\) 对邻接关系与 \(q\) 组 \(\text{LCA}\ ...

随机推荐

  1. (随手记)Javascript 的parseInt函数,在IE和非IE内核浏览器运行的不同结果

    一段JS小程序: var str = "09"; var itr = parseInt(str); alert(itr); IE下运行,alert(0); 火狐和chrome下运行 ...

  2. ON UPDATE CURRENT_TIMESTAMP请慎用

    大家好,我是架构摆渡人.这是实践经验系列的第七篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友. 今天给大家分享一个容易忽略的问题,正是因为容易忽略,所以才要重 ...

  3. Win10编辑Host文件授权问题

    今天重温Kafka命令, 使用KafkaTool连接Broker,需要修改主机名,发现host修改时,提示以下错误: C:\Windows\System32\drivers\etc\hosts.txt ...

  4. vue js 格式化时间

    常用于iview日期组件时间处理 /** * @param {Number} num 数值 * @returns {String} 处理后的字符串 * @description 如果传入的数值小于10 ...

  5. Linux环境下的Docker的安装和部署、学习-一

    CentOS Docker 安装Docker支持以下的CentOS版本:CentOS 7 (64-bit)CentOS 6.5 (64-bit) 或更高的版本 前提条件目前,CentOS 仅发行版本中 ...

  6. npm 和 yarn 前端包管理工具

    前言 前端开发逐渐工程化,npm作为我们的依赖管理工具起到十分重要的作用,本文就来总结一下 npm 和 yarn 相关知识点. 正文 1.什么是npm (1)node的包管理器(node packag ...

  7. vue3源码node的问题

    下载vue3源码后,下载依赖时,node的版本需要在10.0.0以上,并且不同的vue3里面的插件的配置对版本依赖还不同,14.0.0以上的版本基本都不支持win7了, win7系统可以安装12.0. ...

  8. P3224 [HNOI2012]永无乡 题解

    P3224 [HNOI2012]永无乡 题解 题意概括 有若干集合,每个集合最初包含一个值,和一个编号1~n.两个操作:合并两个集合,查询包含值x的集合中第k大值最初的集合编号. 思路 维护集合之间关 ...

  9. es6 快速入门 系列 —— 类 (class)

    其他章节请看: es6 快速入门 系列 类 类(class)是 javascript 新特性的一个重要组成部分,这一特性提供了一种更简洁的语法和更好的功能,可以让你通过一个安全.一致的方式来自定义对象 ...

  10. 初步学习TypeScript

    使用Visual Studio Code搭建TypeScript开发环境 1.TypeScript是干什么的 ? TypeScript是由微软Anders Hejlsberg(安德斯·海尔斯伯格,也是 ...