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

news2024/11/23 0:46:01

题意

求:在所有 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) { // 1
        ll 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) { // 2
        ll 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 { // 3
        ll 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.coloradmin.cn/o/1693774.html

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

相关文章

二叉树——堆详解

目录 前言&#xff1a; 一、堆的结构 二、向上调整和向下调整 2.1 向上调整 2.2 向下调整 2.3 向上调整和向下调整时间复杂度比较 三、堆的实现 3.1 堆的初始化 3.2 堆的销毁 3.3 堆的插入 3.4堆的删除 3.5 取堆顶元素 3.6 对堆判空 四、堆排序 五、TOP-K 问题 六、代码总…

电商公司需不需要建数字档案室呢

建立数字档案室对于电商公司来说是非常有必要的。以下是一些原因&#xff1a; 1. 空间节约&#xff1a;数字档案室可以将纸质文件转化为电子文件&#xff0c;节省了大量存储空间。这对于电商公司来说尤为重要&#xff0c;因为他们通常会有大量的订单、客户信息和供应商合同等文…

Python01:初入Python(Mac)

Python环境准备 下载Python&#xff1a;官网https://www.python.org/ 下载PyCharm&#xff1a;官网https://www.jetbrains.com/pycharm/download Python与PyCharm的关系 Python&#xff08;解释器&#xff09;&#xff1a;机器语言—>翻译人员–>翻译成电脑能读懂的 PyC…

DatePicker日期选择框(antd-design组件库)简单使用

1.DatePicker日期选择框 输入或选择日期的控件。 2.何时使用 当用户需要输入一个日期&#xff0c;可以点击标准输入框&#xff0c;弹出日期面板进行选择。 组件代码来自&#xff1a; 日期选择框 DatePicker - Ant Design 3.本地验证前的准备 参考文章【react项目antd组件-demo:…

2022蓝桥杯大赛软件类国赛Java大学B组 左移右移 空间换时间+双指针

import java.util.Scanner;public class Main {static Scanner scnew Scanner(System.in);public static void main(String[] args) {int nsc.nextInt();//数组长度int tsc.nextInt();//操作次数int arr[]new int[n];char arr1[] new char[t];int arr2[] new int[t];int vis…

金融信贷风控系统设计模式应用之模版方法

背景介绍 风控系统每种场景 如个人消费贷 都需要跑很多规则 规则1 申请人姓名身份证号实名验证规则2 申请人手机号码实名认证规则3 银行卡预留手机号码实名认证规则4 申请人银行卡预留手机号码在网状态检验规则5 申请人银行借记卡有效性核验规则6 户籍地址与身份证号归属地比…

后量子密码解决方案

什么是后量子密码学 (PQC)&#xff0c;为什么准备工作如此重要? 量子计算正在迅速发展;用不了多久&#xff0c;量子网络攻击就会成为可能。量子网络攻击将能够在几分钟内瘫痪大型网络。我们今天赖以保护我们的连接和交易的一切都将受到量子计算机的威胁&#xff0c;危及所有密…

Django中model中的抽象类

Django中model中的抽象类 当我们在app中models.py文件中定义model表并执行python manage.py makemigrations和python manage.py migrate后&#xff0c;Django就会在数据库中创建表 但是我们也可以对其默认配置修改&#xff0c;定义model类但是不在数据库中创建 from django.…

Behind the Code:Polkadot 如何重塑 Web3 未来

2024 年 5 月 17 日 Polkadot 生态 Behind the Code 第二季第一集 《创造 Web3 的未来》正式上线。第一集深入探讨了 Polkadot 和 Web3 技术在解决数字身份、数据所有权和去中心化治理方面的巨大潜力。 &#x1f50d; 查看完整视频&#xff1a; https://youtu.be/_gP-M5nUidc?…

基于Python对评论进行情感分析可视化

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 在当今数字化时代&#xff0c;用户生成内容&#xff08;UGC&#xff09;如在线评论、社交媒体…

需求响应+配网重构!含高比例新能源和用户需求响应的配电网重构程序代码!

前言 配电网重构作为配电网优化运行的手段之一&#xff0c;通过改变配电网的拓扑结构&#xff0c;以达到降低网损、改善电压分布、提升系统的可靠性与经济性等目的。近年来&#xff0c;随着全球能源消耗快速增长以及环境的日趋恶化&#xff0c;清洁能源飞速发展&#xff0c;分…

线性回归模型

目录 1.概述 2.线性回归模型的定义 3.线性回归模型的优缺点 4.线性回归模型的应用场景 5.线性回归模型的未来展望 6.小结 1.概述 线性回归是一种广泛应用于统计学和机器学习的技术&#xff0c;用于研究两个或多个变量之间的线性关系。在本文中&#xff0c;我们将深入探讨…

GM Bali,OKLink受邀参与Polygon AggIsland大会

5月16日-17日&#xff0c;OKLink 受到生态合作伙伴 Polygon 的特别邀请&#xff0c;来到巴厘岛参与以 AggIsland 为主题的大会活动并发表演讲&#xff0c;详细介绍 OKLink 为 Polygon 所带来的包括多个浏览器和数据解析等方面的成果&#xff0c;并与 Polygon 一起&#xff0c;对…

【maven与tomcat配置】如何正确配置maven及tomcat环境变量及运行Java项目 (附图文说明及下载包)

maven及tomcat配置详解 &#x1f354;涉及知识&#x1f964;写在前面&#x1f367;一、maven和tomcat是啥&#xff1f;&#x1f367;二、maven环境变量配置2.1获取maven包2.2创建本地仓库及修改配置A&#xff0e;校验是否安装javaB&#xff0e;创建本地maven存放仓库C&#xff…

C++vector的简单模拟实现

文章目录 目录 文章目录 前言 一、vector使用时的注意事项 1.typedef的类型 2.vector不是string 3.vector 4.算法sort 二、vector的实现 1.通过源码进行猜测vector的结构 2.初步vector的构建 2.1 成员变量 2.2成员函数 2.2.1尾插和扩容 2.2.2operator[] 2.2.3 迭代器 2…

OpenHarmony系统使用gdb调试init

前言 OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;适配新的开发板时&#xff0c;启动流程init大概率会出现问题&#xff0c;其为内核直接拉起的第一个用户态进程&#xff0c;问题定位手段只能依赖代码走读和增加调试打印&#xff0c;初始化过程中系统崩溃…

单片机设计注意事项

1.电源线可以30mil走线&#xff0c;信号线可以6mil走线 2.LDO推荐 SGM2019-3.3,RT9013,RT9193,1117-3.3V。 3.单片机VCC要充分滤波后再供电&#xff0c;可以接0.1uf的电容 4.晶振附件不要走其他元件&#xff0c;且放置完单片机后就放置晶振&#xff0c;晶振靠近X1,X2。

【C++】d1

关键字&#xff1a; 运行、前缀、输入输出、换行 运行f10 前缀必须项&#xff1a; #include <iostream> using namespace std; 输入/输出&#xff1a; cin >> 输入 cout << 输出 语句通过>>或<<分开 换行 endl或者"\n"

前端日志收集(monitor-report v1)

为什么 为什么自己封装而不是使用三方 类似 Sentry 这种比较全面的 因为 Sentry 很大我没安装成功&#xff0c;所有才自己去封装的 为什么使用 可以帮助你简单解决前端收集错误日志、收集当前页面访问量&#xff0c;网站日活跃&#xff0c;页面访问次数&#xff0c;用户行…

Spring ----> IOC

文章目录 一、 Spring 是一个包含众多工具的IoC容器二、 什么是IOC以及好处三、 如何实现loc思想四、Spring提供的实现loC的方法 --- 类注解方法注解4.1 类注解类注解概念介绍类注解的使用 4.2 方法注解Bean 一、 Spring 是一个包含众多工具的IoC容器 场景解析&#xff1a;首先…