JZOJ5870 【NOIP2018模拟9.15】地图

news/2024/11/30 12:40:30/

题目描述

Description

Description

Input

在这里插入图片描述
Output
在这里插入图片描述

Sample Input

4
2 1 1 2

Sample Output

2

Data Constraint

在这里插入图片描述

题目大意

一个无向图,给你 N N N个点的度数,每个点的度数只有 1 1 1 2 2 2,问组成这个图的方案数。


解法

思考历程

非常不爽的是,这是一道计数类问题……
首先有个很显然的规律:最终的图只能由简单环或者链组成。
对于一个度数为 1 1 1的点,它一定是某一条链的一端。
对于一个度数为 2 2 2的点,它要么是链中间的某一个点,要么就是环上的一个点。
我们将 1 1 1度点的总数记为 s 1 s1 s1,将 2 2 2度点的总数记为 s 2 s2 s2。(因为点编号顺序是无论怎样都是等价的)
我首先想到的就是,链的条数是固定的,为 s 1 2 \frac{s1}{2} 2s1(当然 s 1 s1 s1一定是偶数,否则无解)
这条性质,是解题的关键。
所以,试一下DP……
结果因为各种各样的问题爆0

AC解法

其实这题的AC解法很多,DP设的状态五花八门的。
我参考过许多人的方法,但是我并不简单服从,而是想好好推自己的方法,走自己的路。
在我的努力之下,终于AC了。
f i f_i fi表示有 i i i 2 2 2度点放环中的方案数, g i g_i gi表示有 i i i 2 2 2度点放链上的方案数。
先考虑前者。
转移分两种:

  1. 插在某两个相连的点中间
  2. 用三个点新开一个环。

这样转移时不会出现遗漏的。因为这 i i i个点是有自己编号的,它们以一定的顺序插入。并且如果插入的过程不同,那么不可能出现同样的图。
f i = f i − 1 ∗ ( i − 1 ) + f i − 3 ∗ C ( i − 1 , 2 ) f_i=f_{i-1}*(i-1)+f_{i-3}*C\left(i-1,2\right) fi=fi1(i1)+fi3C(i1,2)
!有一个地方要注意一下, C ( i − 1 , 2 ) C\left(i-1,2\right) C(i1,2)中之所以是 i − 1 i-1 i1,是因为 i i i本身必定要放在新环中,所以只能在 i − 1 i-1 i1中选两个,不然会出现重复的情况。
再考虑后者的转移。
只有一种:插在某两个相连的点中间。
g i = g i − 1 ∗ ( i − 1 + s 1 2 ) g_i=g_{i-1}*\left(i-1+\frac{s1}{2}\right) gi=gi1(i1+2s1)
除了要计算出两个数组外,还要另外计算 s 1 s1 s1中两两匹配的方案数。
考虑使用组合数。
第一次在 s 1 s1 s1中选 2 2 2个,第二次在 s 1 − 2 s1-2 s12中选 2 2 2个……第 s 1 2 \frac{s1}{2} 2s1次在 2 2 2中选 2 2 2个。
去重,对于每个情况有 s 1 2 ! \frac{s1}{2} ! 2s1!种排列,所以除掉它。
方案数为
∏ i = 0 s 1 − 1 C ( s 1 − 2 i , 2 ) s 1 2 ! \frac{\prod_{i=0}^{s1-1} C\left(s1-2i,2\right)}{\frac{s1}{2} !} 2s1!i=0s11C(s12i,2)
化简得
∏ i = 0 s 1 − 1 ( 2 i + 1 ) \prod_{i=0}^{s1-1}(2i+1) i=0s11(2i+1)
怎么化简的……自己想去吧……
统计答案时,枚举 i i i表示有 i i i个2度点。
f i f_i fi g s 2 − i g_{s2-i} gs2i合并在一起,另外乘上两两匹配的方案数,还有 s 2 s2 s2中选出 i i i个的方案数。
这样时间复杂度可以做到 O ( N 2 ) O\left(N^2\right) O(N2)

还能更优?

上面这个时间是求组合数的时间。
然而我们可以预先处理出阶乘来算组合数,除法用逆元,时间 O ( N lg ⁡ M O D ) O\left(N\lg MOD\right) O(NlgMOD)
逆元其实可以 O ( N ) O\left(N\right) O(N)预处理出来,那么时间 O ( N ) O\left(N\right) O(N)
所以这题的范围太小了……
不过我不需要用到阶乘。
考虑 C ( i , s 2 ) C\left(i,s2\right) C(i,s2) C ( i + 1 , s 2 ) C\left(i+1,s2\right) C(i+1,s2)的贡献。
推推式子容易得出
C ( i , s 2 ) ∗ s 2 − i i + 1 = C ( i + 1 , s 2 ) C\left(i,s2\right)*\frac{s2-i}{i+1}=C\left(i+1,s2\right) C(i,s2)i+1s2i=C(i+1,s2)
所以我们只预处理出逆元,然后计算组合数时通过上一个 O ( 1 ) O(1) O(1)推出下一个就好了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 2000
#define mod 998244353
int n;
int inv[MAXN+1];
int s1,s2;
int m;
int f[MAXN+1];
int g[MAXN+1];
int main(){freopen("map.in","r",stdin);freopen("map.out","w",stdout);scanf("%d",&n);for (int i=1;i<=n;++i){int x;scanf("%d",&x);if (x==1)s1++;elses2++;}inv[0]=0;inv[1]=1;for (int i=2;i<=s2;++i)inv[i]=(long long)(mod-mod/i)*inv[mod%i]%mod; //O(N)求逆元,具体证明网上大把f[0]=1;for (int i=3;i<=s2;++i)f[i]=((long long)f[i-1]*(i-1)+f[i-3]*(((long long)(i-1)*(i-2)>>1)%mod))%mod;//具体见上f的转移g[0]=1;for (int i=1;i<=s2;++i)g[i]=(long long)g[i-1]*(i-1+(s1>>1))%mod;//具体见上g的转移int matching=1;//表示两两匹配数for (int i=1;i<s1;i+=2)matching=(long long)matching*i%mod;int ans=0,C=1;for (int i=0;i<=s2;++i){ans=(ans+(long long)f[i]*g[s2-i]%mod*C)%mod;//统计答案C=(long long)C*(s2-i)%mod*inv[i+1]%mod;//推出下一个组合数}ans=(long long)ans*matching%mod;//最后乘上两两匹配数printf("%d\n",ans);return 0;
}

总结

如何在计数类问题中,做到不重复,不遗漏才是关键……
组合数大法好……


http://www.ppmy.cn/news/264156.html

相关文章

UVALive 5870 - Smooth Visualization

题目大意&#xff1a;任何一个1~7的数由竖着一列字符表示&#xff0c;从下到上数是几就有几个‘’&#xff0c;空余地方是‘*’&#xff0c;现在给定一行数&#xff0c;将这行数用一个矩阵表示出来&#xff0c;要求&#xff0c;相邻两列之间‘’个数不能相差超过1&#xff0c;超…

[dp] Jzoj P5870 地图

Description Input Output Sample Input 4 2 1 1 2 Sample Output 2 Data Constraint 题解 设f[i][j]为剩下i个1&#xff0c;j个2的地图数量如果2号用了一条边就是1号点了根据情况dp 代码 1 #include <cstdio>2 using namespace std;3 int n,num[3],f[2010][2010],mo9982…

HDU5870 Alice's Adventure in Wonderland

大概做法是这样的 考虑最朴素的做法&#xff0c;预处理出1到所有点的最短路数组dis1和方案数数组cnt1&#xff0c;和预处理出n到所有点的最短路数组dis2和方案数数组出cnt2,然后暴力枚举点对(A,B),如果A和B之间没有连边&#xff0c;那么就可以考虑添加一条正权边&#xff0c;满…

【JZOJ5870】地图

Description 给定N个度数为1或2的点&#xff0c;求所有带标号简单无向图&#xff08;无重边和自环&#xff09;的方案数。 Solution 设度数为2的点有 n n n个&#xff0c;度数为1的有 m m m个。 因为只有1和2&#xff0c;所以图一定是由许多链和大小大于等于3的简单环构成的…

RDA5870调试

时间&#xff1a;20100506 现象&#xff1a;我司准备在MTK6253上换蓝牙芯片为RDA5870. 调试过程&#xff1a; 1、首先将RDA5870给的相关代码放到我们自己的工程中&#xff0c;编译通过。但是在添加的时候要注意用相关的宏名控制。 首先在工程文件中添加&#xff1a; RDA_BLUETO…

mysqldump 逻辑备份的正确姿势

在上一篇文章 MySQL 命令行工具之 mysqldump 深入研究 中&#xff0c;我们搞定了mysqldump的参数和基本原理。那么我们该怎么样最好的使用它的&#xff1f;它有哪些坑呢&#xff1f; 1. 利用mysqldump进行逻辑备份 1&#xff09;全逻辑备份&#xff1a; mysqldump -uxxx -p --f…

AMD OpenCL 大学课程

AMD OpenCL大学课程是非常好的入门级OpenCL教程&#xff0c;通过看教程中的PPT&#xff0c;我们能够很快的了解OpenCL机制以及编程方法。下载地址&#xff1a;http://developer.amd.com/zones/OpenCLZone/universities/Pages/default.aspx 教程中的英文很简单&#xff0c;我相信…

【Linux】软硬链接

文章目录 制作软硬链接&#xff0c;对比差别提出软硬链接的应用场景总结 制作软硬链接&#xff0c;对比差别 制作软链接 1.先创建一个文件&#xff0c;并向其中写入内容&#xff1a; 2.建立软链接&#xff1a;ln -s myfile.txt my-soft 可以发现&#xff0c;软链接是一个独…