题解:P9535 [YsOI2023] 连通图计数

题意

求:在所有 n n n 个点 m m m 条边的无向简单连通图中,满足把第 i i i 个点删去后图被分为 a i a_i ai​ 个连通块。

n − 1 ≤ m ≤ n + 1 n-1\le m\le n+1 n1mn+1

思路

m = n − 1 , m = n , m = n + 1 m=n-1,m=n,m=n+1 m=n1,m=n,m=n+1​ 三种情况进行分类讨论。

对于 m = n − 1 m=n-1 m=n1,显然是一棵树,每个 a i a_i ai 即为 i i i 的子树数量+父亲。此时需要用到 Prufer 序列(可以看看这篇博客),答案为: n n n 个点的完全生成树中第 i i i 个节点的度数为 a i a_i ai 的方案数,即为:
( n − 2 ) ! ∏ i = 1 n ( a i − 1 ) ! \cfrac{(n-2)!}{\prod^n_{i=1}(a_i-1)!} i=1n(ai1)!(n2)!
m = n m=n m=n 时,相当于在树上加上一条边形成一个环。我们把环上的边都删除,开一个编号为 n + 1 n+1 n+1 的新点与环上的点连边。这样原图就又转化成了一棵 n + 1 n+1 n+1 个点 n n n 条边的树,而第 n + 1 n+1 n+1​ 个点的度数为环上点的个数。举个例子:

  • 这是一张 6 6 6 个点 6 6 6 条边的图,其中红色数组代表 a i a_i ai 的值。而环的大小为 4 4 4

可以发现:环的大小就是点数乘二再减去度数之和,即
a n + 1 = 2 n − ∑ i = 1 n a i a_{n+1}=2n-\sum^n_{i=1}a_i an+1=2ni=1nai
根据上一种情况,再乘上环上点的排列方案 A a n + 1 a n + 1 2 a n + 1 = ( 2 n − ( ∑ i = 1 n a i ) − 1 ) ! 2 \cfrac{A^{a_{n+1}}_{a_{n+1}}}{2a_{n+1}}=\cfrac{\big(2n-(\sum^n_{i=1}a_i\big)-1)!}{2} 2an+1Aan+1an+1=2(2n(i=1nai)1)!,答案即为
( n − 1 ) ! ( ∏ i = 1 n ( a i − 1 ) ! ) ( 2 n − ( ∑ i = 1 n a i ) − 1 ) ! × ( 2 n − ( ∑ i = 1 n a i ) − 1 ) ! 2 = ( n − 1 ) ! 2 ∏ i = 1 n ( a i − 1 ) ! \begin{aligned}&~~~~~\cfrac{(n-1)!}{\big(\prod^n_{i=1}(a_i-1)!\big)(2n-\big(\sum^n_{i=1}a_i)-1\big)!}\times\cfrac{(2n-(\sum^n_{i=1}a_i)-1)!}{2}\\&=\cfrac{(n-1)!}{2\prod^n_{i=1}(a_i-1)!}\end{aligned}      (i=1n(ai1)!)(2n(i=1nai)1)!(n1)!×2(2n(i=1nai)1)!=2i=1n(ai1)!(n1)!
m = n + 1 m=n+1 m=n+1 时,相当于在上一种情况再多加一个环。进行分类讨论:

  • 两个环无公共边

此时我们可以将两个环分别缩成 n + 1 , n + 2 n+1,n+2 n+1,n+2 两个点,仿照 m = n m=n m=n 进行连边,最终会得到 n + 2 n+2 n+2 个点 n + 1 n+1 n+1​ 条边的树。再举个例子:

  • 这是一张 9 9 9 个点 10 10 10 条边的图,左环大小为 5 5 5,右环大小为 4 4 4

区别在于,此时只能算出两个环的大小之和,答案类似,即
a n + 1 + a n + 2 = 2 n − ∑ i = 1 n a i + 2 a_{n+1}+a_{n+2}=2n-\sum^n_{i=1}a_i+2 an+1+an+2=2ni=1nai+2
我们设 2 n − ∑ i = 1 n a i = s u m 2n-\sum^n_{i=1}a_i=sum 2ni=1nai=sum。两点的度数都至少为 3 3 3(否则不成环),我们枚举 a n + 1 a_{n+1} an+1 j j j,则 a n + 2 = s u m + 2 − j a_{n+2}=sum+2-j an+2=sum+2j。仿照 m = n − 1 m=n-1 m=n1 的求法,此时树的数量为
( n + 2 − 2 ) ! ∏ i = 1 n + 2 ( a i − 1 ) ! = n ! ( ∏ i = 1 n ( a i − 1 ) ! ) ( j − 1 ) ! ( s u m − j + 1 ) ! \cfrac{(n+2-2)!}{\prod^{n+2}_{i=1}(a_i-1)!}=\cfrac{n!}{\big(\prod^n_{i=1}(a_i-1)!\big)(j-1)!(sum-j+1)!} i=1n+2(ai1)!(n+22)!=(i=1n(ai1)!)(j1)!(sumj+1)!n!
但我们需要保证 n + 1 n+1 n+1 n + 2 n+2 n+2 在树中无连边(即两个环没有公共点,否则就变成一个环了)。对于有连边的情况,我们按照 m = n m=n m=n 建出 n + 1 n+1 n+1 个点 n n n 条边的树来,则这棵树的 a n + 1 = s u m a_{n+1}=sum an+1=sum,树的数量即为
( n − 1 ) ! ( ∏ i = 1 n ( a i − 1 ) ! ) ( 2 n − ( ∑ i = 1 n a i ) − 1 ) ! = ( n − 1 ) ! ( ∏ i = 1 n ( a i − 1 ) ! ) ( s u m − 1 ) ! \cfrac{(n-1)!}{\big(\prod^n_{i=1}(a_i-1)!\big)\big(2n-(\sum^n_{i=1}a_i)-1\big)!}=\cfrac{(n-1)!}{\big(\prod^n_{i=1}(a_i-1)!\big)(sum-1)!} (i=1n(ai1)!)(2n(i=1nai)1)!(n1)!=(i=1n(ai1)!)(sum1)!(n1)!
而这样的树每个都有 C s u m j − 1 = s u m ! ( s u m − j + 1 ) ! ( j − 1 ) ! C^{j-1}_{sum}=\cfrac{sum!}{(sum-j+1)!(j-1)!} Csumj1=(sumj+1)!(j1)!sum!​ 种,则所有不合法的树的数量为
( n − 1 ) ! ( ∏ i = 1 n ( a i − 1 ) ! ) ( s u m − 1 ) ! × s u m ! ( s u m − j + 1 ) ! ( j − 1 ) ! \cfrac{(n-1)!}{\big(\prod^n_{i=1}(a_i-1)!\big)(sum-1)!}\times\cfrac{sum!}{(sum-j+1)!(j-1)!} (i=1n(ai1)!)(sum1)!(n1)!×(sumj+1)!(j1)!sum!
化简得
( n − 1 ) ! s u m ( ∏ i = 1 n ( a i − 1 ) ! ) ( j − 1 ) ! ( s u m − j + 1 ) ! \cfrac{(n-1)!sum}{\big(\prod^n_{i=1}(a_i-1)!\big)(j-1)!(sum-j+1)!} (i=1n(ai1)!)(j1)!(sumj+1)!(n1)!sum
使用容斥,所有合法的树的数量等于所有减去不合法,即为
( n − 1 ) ! ( n − s u m ) ( ∏ i = 1 n ( a i − 1 ) ! ) ( j − 1 ) ! ( s u m − j + 1 ) ! \cfrac{(n-1)!(n-sum)}{\big(\prod^n_{i=1}(a_i-1)!\big)(j-1)!(sum-j+1)!} (i=1n(ai1)!)(j1)!(sumj+1)!(n1)!(nsum)
同理,还要算上两个环的排列方案共 ( j − 1 ) ! ( s u m − j + 1 ) ! 4 \cfrac{(j-1)!(sum-j+1)!}{4} 4(j1)!(sumj+1)!,则答案为
( n − 1 ) ! ( n − s u m ) ( ∏ i = 1 n ( a i − 1 ) ! ) ( j − 1 ) ! ( s u m − j + 1 ) ! × ( j − 1 ) ! ( s u m − j + 1 ) ! 4 \cfrac{(n-1)!(n-sum)}{\big(\prod^n_{i=1}(a_i-1)!\big)(j-1)!(sum-j+1)!}\times\cfrac{(j-1)!(sum-j+1)!}{4} (i=1n(ai1)!)(j1)!(sumj+1)!(n1)!(nsum)×4(j1)!(sumj+1)!
化简得
( n − 1 ) ! ( n − s u m ) 4 ∏ i = 1 n ( a i − 1 ) ! \cfrac{(n-1)!(n-sum)}{4\prod^{n}_{i=1}(a_i-1)!} 4i=1n(ai1)!(n1)!(nsum)
最终我们发现对于不同的 j j j 最终答案相同,一共枚举了 s u m + 2 − 3 − 3 + 1 = s u m − 3 sum+2-3-3+1=sum-3 sum+233+1=sum3 次,考虑到两个环位置可以调换但属同一种情况,所以答案要除以二,即为
( n − 1 ) ! ( n − s u m ) ( s u m − 3 ) 8 ∏ i = 1 n ( a i − 1 ) ! \cfrac{(n-1)!(n-sum)(sum-3)}{8\prod^{n}_{i=1}(a_i-1)!} 8i=1n(ai1)!(n1)!(nsum)(sum3)

  • 两个环有公共边

将这两个挨在一起的环看作一个点,构造一个与 m = n m=n m=n 时的树,树的数量也相同。现在考虑这挨在一起的两个环的方案。我们可以将这两个环拆成类似韦恩图的样子,分为左边环独有部分、左右环公用部分、右边环都有部分三条链。因为要有公共边,所以有两条及以上的链中边数不大于 1 1 1 显然不合法。而这三条链头尾都是相同的。

因为环挨在一起,所以这两个环上节点一共有 s u m = 2 n − ∑ i = 1 n a i sum=2n-\sum^n_{i=1}a_i sum=2ni=1nai 个点。我们先从 s u m sum sum 中挑两个点出来,之后的每个点都选择两个位置放在中间,不合法数量(即之后的所有点全都放在一条链上)即为 3 ( s u m − 2 ) ! 3(sum-2)! 3(sum2)!​;而放入点的顺序并不影响最终答案。故答案为
s u m ( s u m − 1 ) 2 × s u m ! 2 − 3 ( s u m − 2 ) ! 3 ! = s u m ! 24 ( s u m ( s u m − 1 ) − 6 ) = s u m ! 24 ( s u m + 2 ) ( s u m − 3 ) \begin{aligned}\cfrac{sum(sum-1)}{2}\times\cfrac{\frac{sum!}{2}-3(sum-2)!}{3!}&=\cfrac{sum!}{24}(sum(sum-1)-6)\\&=\cfrac{sum!}{24}(sum+2)(sum-3)\end{aligned} 2sum(sum1)×3!2sum!3(sum2)!=24sum!(sum(sum1)6)=24sum!(sum+2)(sum3)
再乘上树的数量,答案为
s u m ( s u m + 2 ) ( s u m − 3 ) ( n − 1 ) ! 24 ∏ i = 1 n ( a i − 1 ) ! \cfrac{sum(sum+2)(sum-3)(n-1)!}{24\prod^n_{i=1}(a_i-1)!} 24i=1n(ai1)!sum(sum+2)(sum3)(n1)!
两种情况分别计算,最后相加即可。复杂度 O ( n ) O(n) O(n)

实现

预处理出阶乘,逆元用快速幂计算,三种情况分别处理即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e6 + 5;
const int P = 998244353;
int n,m; ll a[maxn]; ll pro[maxn];
ll inv(ll x) {int y = P - 2; ll res = 1;while (y) {if (y & 1) res = (res * x) % P;x = (x * x) % P, y >>= 1;}return res;
}
int main() {pro[0] = pro[1] = 1;for (ll i = 2;i <= maxn - 5;i ++) pro[i] = (pro[i - 1] * i) % P;scanf("%d%d",&n,&m);if (m == n - 1) { // 1ll tmp = 1;for (int i = 1;i <= n;i ++)scanf("%lld",&a[i]),tmp = (tmp * pro[a[i] - 1]) % P;printf("%lld",pro[n - 2] * inv(tmp) % P);} else if (m == n) { // 2ll tmp = 2;for (int i = 1;i <= n;i ++)scanf("%lld",&a[i]),tmp = (tmp * pro[a[i] - 1]) % P;printf("%lld",pro[n - 1] * inv(tmp) % P);} else { // 3ll sum = n * 2 % P, tmp = 1;for (int i = 1;i <= n;i ++)scanf("%lld",&a[i]),sum -= a[i], tmp = (tmp * pro[a[i] - 1]) % P;ll ans1 = ((pro[n - 1] * (n - sum) % P) * (sum - 3) % P) * inv(tmp * 8 % P) % P;ll ans2 = ((((pro[n - 1] * (sum + 2) % P) * (sum - 3) % P) * sum) % P) * inv(tmp * 24 % P) % P;printf("%lld",(ans1 + ans2) % P);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/15439.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从git上拉取项目进行操作

1.Git的概念 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速的处理从很小到非常大的项目版本管理。它实现多人协作的机制是利用clone命令将项目从远程库拉取到本地库&#xff0c;做完相应的操作后再利用push命令从本地库将项目提交至远程库。 2.Git的工作流程…

iMX6ULL 嵌入式linux开发 | 4G无线广播终端实现方案介绍

现有的有线广播&#xff0c;如村上的大喇叭&#xff0c;需要布线&#xff0c;施工麻烦。借助现有的4G网络&#xff0c;传输音频流完全没问题&#xff0c;4G网络结合流媒体技术和MQTT消息传递实现设备间的同步推拉流。这种方案可以避免有线布线的麻烦&#xff0c;同时实现4G无线…

Mysql插入中文内容报错解决及其Mysql常用的存储引擎说明

一、问题描述 我们在Mysql数据库的表中插入带有中文内容时报错,提示【1366 - Incorrect string value: \xE5\x8C\x97\xE4\xBA\xAC... for column UserDealer at row 1】,如下图所示: 二、问题分析 一般来说插入中文内容有问题我们首先想到的就是编码问题;我们可以查看该表使…

【Python】 如何在Python中导入其他Python文件?

基本原理 在Python编程中&#xff0c;我们经常需要将代码组织成模块&#xff0c;以便于重用和维护。模块是包含Python定义和语句的文件。导入模块可以让你访问其他文件中定义的函数、类和变量等。Python提供了几种不同的方法来导入模块。 代码示例 示例1&#xff1a;导入整个…

超值分享50个DFM模型格式的素人直播资源,适用于DeepFaceLive的DFM合集

50直播模型&#xff1a;点击下载 作为直播达人&#xff0c;我在网上购买了大量直播用的模型资源&#xff0c;包含男模女模、明星脸、大众脸、网红脸及各种稀缺的路人素人模型。现在&#xff0c;我将这些宝贵的资源整理成合集分享给大家&#xff0c;需要的朋友们可以直接点击下…

在线生成数据库er图的工具

网址 https://databasediagram.com/ 其实很早之前我也有类似的想法&#xff0c;根据数据表结构&#xff0c;显示数据表之间的关系图。 当时我还写了一个工具&#xff0c;可惜后来就没怎么用过了。 这个网站和我当时的思路很像&#xff0c;只不过他这个页面显示比我的好得多&…

苍穹外卖--sky-take-out(一)

目录 d1 软件开发流程 项目介绍 产品原型 技术选型 前端环境搭建 后端环境搭建 Git版本控制 数据库环境搭建 nginx反向代理和负载均衡 导入接口文档 Swagger 问题 d2 用户登录 代码实现 MD5密码加密 新增员工 需求分析与设计 代码开发 代码完善&#xff08;Threa…

ACW石子合并-XMUOJ元素共鸣:唤醒神之眼 -区间DP

题目 思路 话不多说&#xff0c;直接上代码 代码 /* ACW石子合并-XMUOJ元素共鸣&#xff1a;唤醒神之眼 JinlongW-2024/05/25 区间DP 当i<j时&#xff0c;f[i][j]min(f[i][k]f[k][j]s[j]-s[i-1]) 当ij时&#xff0c;f[i][j]0 最终答案&#xff1a;f[1][n] *//* 区间DP…

maven-依赖管理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Maven BOM二、使用三、SpringBoot的依赖管理 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 依赖管理能带来啥&#xff1a; 避免…

linux文件权限常用知识点,基于Linux(openEuler、CentOS8)

目录 知识点常用实例 知识点 真实环境文件显示 解读 常用实例 文件所有者 chown -R nginx:nginx /home/source目录权限(R选填必须大写<遍历子文件夹及文件>) chmod -R 755 /home/sourcechmod -R 777 /home/source

如何使用甘特图来做任务管理?zz-plan甘特图的实践指南

在项目管理和任务调度中&#xff0c;甘特图是一种非常实用的工具&#xff0c;它可以帮助团队成员清晰地规划、执行和跟踪项目进度。然而&#xff0c;如何有效利用甘特图进行任务管理&#xff0c;对于许多团队来说仍然是一个挑战。本文将结合 zz-plan https://zz-plan.com/ 甘特…

重学java 44.多线程 Lock锁的使用

昨日之深渊&#xff0c;今日之浅谈 —— 24.5.25 一、Lock对象的介绍和基本使用 1.概述 Lock是一个接口 2.实现类 ReentrantLock 3.方法 lock()获取锁 unlock()释放锁 4.Lock锁的使用 package S78Lock;import java.util.concurrent.locks.Lock; import java.util.concurrent.lo…

【机器学习】大模型在机器学习中的应用:从深度学习到生成式人工智能的演进

&#x1f512;文章目录&#xff1a; &#x1f4a5;1.引言 ☔2.大模型概述 &#x1f6b2;3.大模型在深度学习中的应用 &#x1f6f4;4.大模型在生成式人工智能中的应用 &#x1f44a;5.大模型的挑战与未来展望 &#x1f4a5;1.引言 随着数据量的爆炸性增长和计算能力的提…

【C++】类与对象——多态详解

目录 一、多态的定义 二、重载、覆盖(重写)、隐藏(重定义)的对比 三、析构函数重写 四、C11 override 和 final 1. final 2. override 五、抽象类 六、多态的原理 一、多态的定义 多态是在不同继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的行为…

访存优化实践之一 : CPU、GPU、DDR与访存路径介绍

一、CPU的访存路径 上图是目前主流的CPU架构介绍。可以看到,CPU的访存路径:先经过MMU,然后经过Cache,最后到达DRAM。这其中涉及到的关键内容为基于MMU的内存管理以及缓存机制。 1.1、基于MMU的内存管理 众所周知,在计算机设计之处是没有虚拟地址的概念的,CPU发出的地址即…

centos7.9用docker运行一个nginx容器

首先你的linux 系统里面已经安装好了docker&#xff0c;docker的安装教程看这个 1&#xff0c;下载nginx镜像 有很多文章会把镜像下载说成是拉取镜像&#xff0c; 我觉得就是下载的意思啊&#xff0c;搞不懂为什么要说拉取&#xff1f; docker pull nginx 下载最新版 Nginx …

LeetCode刷题笔记第2769题:找到最大的可达成数字

LeetCode刷题笔记第2769题&#xff1a;找到最大的可达成数字 题目&#xff1a; 想法&#xff1a; 从题目中可以看出&#xff0c;num经过t次增减变为x&#xff0c;x即为可达成数字。因为要求最大的可达成数字&#xff0c;需要满足num一直增加&#xff0c;x一直减少&#xff0c…

蛮力法0/1背包问题实验

实验项目1 蛮力法 实验题目 使用蛮力法解决0/1背包问题。 ​ 问题描述&#xff1a;给定n个重量(weight)为{w1, w2, … ,wn}、价值(key)为{v1, v2, … ,vn}的物品和一个**容量为C(contain)**的背包&#xff0c;求这些物品中的一个最有价值的子集&#xff0c;且要能够装到背包中…

【简单介绍下链表基础知识】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

vue data中的return

vue 的data return 是干啥的呢&#xff0c;vue中页面中绑定的变量都要放在data的return中&#xff0c;可以赋值&#xff0c;值可在script中改&#xff0c;修改引用就用this了 如果不使用return包裹的数据会在项目的全局中可见&#xff0c;会造成变量污染&#xff1b; 使用retu…