【JZOJ5870】【NOIP2018模拟9.15】地图 (递推+DP+组合数学)

news/2024/11/30 12:35:09/

Problem

这里写图片描述

Hint

这里写图片描述

Solution

  • 首先,图中只会存在链和环。
  • 记图中有one个度数为1的点,two个度数为2的点。囿于每条链有两个度数为1的点(链的两端),链的数量是确定的: o n e 2 \frac{one}2 2one
  • 这时,我灵(nao)光(zi)一(wa)闪(te),想到了一个优(sha)美(bi)的方法。

我的SB方法:递推+组合数学+容斥

  • 观察到链和环的方案相对独立。那么根据乘法原理,我们可以求出链的方案tot1,环的方案tot2,总方案即为tot1*tot2。
  • 那么分类讨论一下。
链的情况
  • 我们先令one个点两两匹配,构成 o n e 2 \frac{one}2 2one条只含两个点的链。
  • 不妨枚举当前有n条链。假设增加一条,则点数增加为2*n+2。
  • 枚举点1连接的是哪个点,这有2n+1种可能;而剩下的2n个点,两两匹配成i条链。
  • 因此,递推式为 h n + 1 = ( 2 ∗ n + 1 ) ∗ h n h_{n+1}=(2*n+1)*h_n hn+1=(2n+1)hn,其中n为链数。

  • 现在,我们再将度数为2的点插入到这些链中。
  • g i g_i gi表示有i个度数为2的点在链中。新插一个点,我们可以插在所有链的非链首节点的左边。譬如下图:
    在这里插入图片描述
  • 我们可以插在任意一个蓝点左边,因此有3+2=5种方案。实际上,记有k条链,第i条链的链长为 l e n i len_i leni,插入新点的方案即为 ∑ i = 1 k l e n i − 1 \sum_{i=1}^k len_i-1 i=1kleni1。 不难发现,这其实等于已插入的点数+链数
  • 因此, g i = g i − 1 ∗ ( i − 1 + o n e 2 ) g_i=g_{i-1}*(i-1+\frac{one}2) gi=gi1(i1+2one),其中 o n e 2 \frac{one}2 2one为链数。

环的情况

  • 环的情况就有些复杂了。
  • 考虑DP。设 f i , j f_{i,j} fi,j表示i个度数为2的点在环上,其中有j个一元环的方案数。
  • 囿于原图不存在自环,我们最终得到的应是 f i , 0 f_{i,0} fi,0
  • 然后可以得到三种转移:1.新建一个一元环;2.令当前点加入到一个一元环中;3.令当前点加入到一个多元环中。

  • 然而还有一个坑点——那就是环翻转一下,和原来全等,但是我们会算重。
  • 不妨在新建环的时候,就将其贡献记为 1 2 \frac 12 21,这样最终算出的结果便是去了重的。
  • 然而,我们这样只能算出无自环的方案数,不能算出无二元环(重边)的方案数。

  • 考虑容斥。
  • 枚举有i个度数为2的点在环上,其中有j个二元环。那么正负性为 ( − 1 ) j (-1)^j (1)j,系数为 C i 2 j ∗ h j ∗ 2 − j C_i^{2j}*h_j*2^{-j} Ci2jhj2j,其中 h j h_j hj为上述提到的,点两两匹配的方案数。
  • 系数中有个 2 − j 2^{-j} 2j的原因是我们把二元环的贡献都算成了 1 2 \frac 12 21(建环时是 1 2 \frac 12 21,再插一个点是1, 1 2 ∗ 1 = 1 2 \frac 12*1=\frac 12 211=21),然而二元环的贡献应是1;于是在去掉二元环的方案中,我们应该也乘回这些 1 2 \frac 12 21以弥补二元环的缺失。
  • 然后对于每个i,都求一波 ∑ j = 0 ⌊ i 2 ⌋ ( − 1 ) j ∗ C i 2 j ∗ h j ∗ 2 − j ∗ f i − 2 j , 0 \sum_{j=0}^{\lfloor \frac i2 \rfloor} (-1)^j*C_i^{2j}*h_j*2^{-j}*f_{i-2j,0} j=02i(1)jCi2jhj2jfi2j,0,而这便是真正的 f i f_i fi(无自环、无二元环的方案数)。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)

Code

#include <bits/stdc++.h>
#define P(x,y) x=((x)+(y))%mo
#define T(x,y) x=((x)*(y))%mo
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;const int N=4001;
const ll mo=998244353;
int k,n,d,one,two;
ll i,j,ls,dou[N],lian[N],div2,f[N][N],g,xs,C[N][N],zf,ans;ll fpow(ll x,ll y)
{ll ans=1;for(;y;y>>=1,T(x,x)) if(y&1)T(ans,x);return ans;
}int main()
{freopen("map.in","r",stdin);freopen("map.out","w",stdout);scanf("%d",&n);fo(i,1,n) {scanf("%d",&d);d&1 ? one++ : two++;}if(one&1) {puts("0"); return 0;}dou[0]=1;fo(i,1,n) dou[i]=dou[i-1]*(i*2-1)%mo;lian[0]=dou[ls=one>>1];fo(i,1,two) lian[i]=lian[i-1]*(ls+i-1)%mo;f[0][0]=1; div2=fpow(2,mo-2);fo(i,0,two-1)fo(j,0,i)if(f[i][j]){P(f[i+1][j+1],f[i][j]*div2);P(f[i+1][j],f[i][j]*(i-j));if(j) P(f[i+1][j-1],f[i][j]*j);}fo(i,0,two){C[i][0]=1;fo(j,1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;}fo(i,0,two) {g=0;fo(j,0,i>>1){xs=C[i][j<<1]*dou[j]%mo*fpow(div2,j)%mo;zf=(j&1?-1:1);P(g,zf*xs*f[i-j*2][0]); P(g,mo);}P(ans,g*C[two][i]%mo*lian[two-i]);}printf("%lld",ans);
}

一个更为舒服的方法

  • 实际上,这道题一个DP就解决了。
  • f i , j f_{i,j} fi,j表示i个度数为2的点,其中j个点在环上(即剩下的i-j个点在链上)的方案数。
  • 有以下三种转移:
    f i , j = { f i − 3 , j − 3 ∗ C i − 1 2 新 建 三 元 环 f i − 1 , j − 1 ∗ ( j − 1 ) 将 点 i 插 入 一 个 环 中 f i − 1 , j ∗ ( i − j − 1 + o n e 2 ) 将 点 i 插 入 一 条 链 中 f_{i,j}=\left\{ \begin{aligned} &amp;f_{i-3,j-3} *C_{i-1}^2 &amp; &amp; 新建三元环 \\ &amp;f_{i-1,j-1} *(j-1) &amp; &amp; 将点i插入一个环中 \\ &amp;f_{i-1,j} \ \ \ *(i-j-1+\frac{one}2) &amp; &amp; 将点i插入一条链中 \end{aligned} \right. fi,j=fi3,j3Ci12fi1,j1(j1)fi1,j   (ij1+2one)ii
  • 新建环的贡献为何是 C i − 1 2 C_{i-1}^2 Ci12呢?我们假定点i就在新环内,然后从剩下的i-1个点中选2个出来陪它。如若不然,则有可能算重。然后不必再因会翻转除以2,因为三元环定是唯一的。

  • 这样的话, a n s = ∑ i = 0 t w o f t w o , i ans=\sum_{i=0}^{two} f_{two,i} ans=i=0twoftwo,i,其中two为度数为2的节点的个数。
  • 但还没把链首、链尾两两匹配的方案数算上,所以最后要再乘上。
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)

Code

#include <bits/stdc++.h>
#define P(x,y) x=(x+y)%mo
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;const int N=2001;
const ll mo=998244353;
int n,d,two;
ll i,j,one,f[N][N],ans;int main()
{freopen("map.in","r",stdin);freopen("map.out","w",stdout);scanf("%d",&n);fo(i,1,n){scanf("%d",&d);d&1?one++:two++;}if(one&1) {puts("0"); return 0;}f[0][0]=1;fo(i,1,two)fo(j,0,i){if(j>=3) f[i][j]=f[i-3][j-3]*((i-1)*(i-2)>>1)%mo;if(j>=1) P(f[i][j],f[i-1][j-1]*(j-1));P(f[i][j],f[i-1][j]*(one/2+i-j-1));}fo(i,0,two) P(ans,f[two][i]);fo(i,3,one) if(i&1) (ans*=i)%=mo;printf("%lld",ans);
}

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

相关文章

Smooth Visualization(UVALive 5870)

题意&#xff1a; 给一个长度不超过100的整数字符串&#xff0c;给每一列输出‘*’和‘’&#xff0c;并且两列加号数量的差要小于等于1(*在列的上面)。也就在两个字符间加一些数字&#xff0c;让两个字符呈升序或降序&#xff0c;然后用‘* 和‘’表示出来(如果两个字符相同或…

【瑞格】c语言文件操作实例( 【5870】 内容的加密以及文件写入、读取)

输入文件内容&#xff1a; Hello my Dear: This is a secret, so don’t tell anyone else! Mr. Right is LiXiaoMing. ac代码&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h>int main() {FILE *fp,*fq;char s[150];int i;if((fp…

JZOJ5870 【NOIP2018模拟9.15】地图

题目描述 Description Input Output Sample Input 4 2 1 1 2 Sample Output 2 Data Constraint 题目大意 一个无向图&#xff0c;给你 N N N个点的度数&#xff0c;每个点的度数只有 1 1 1或 2 2 2&#xff0c;问组成这个图的方案数。 解法 思考历程 非常不爽的是&#xff…

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…