jzoj 5351. 【NOIP2017提升A組模擬9.7】簡單無向圖

Description

Input

Output

Solution

這題有好幾個作法,這裏介紹兩個spa

Solution 1

(這彷佛是網上的正解)
From zsjzliziyang.net

設f[ i ][ j ]表示度數爲一、2的點個數分別爲i,j時答案。
分爲下面的4種狀況
1:當t2爲0時咱們新增若干條長度爲2的鏈,咱們將不會在這些鏈中間加其餘任何點
2:新增形如1-2-1的一條新鏈
3:在條鏈的一個位置加入兩個爲2的點
4:把某一條鏈拆掉,將其中度數爲2的點與3個新的點構成一個新的環
這個方法最關鍵的地方就是它是把一條鏈中間的度數爲2的點和3個新的點構成一個環,很大程度上避免了在環中間加入新的數致使很難搞重複的窘境code

而後進行DP便可
但由於本題有更快的方法,因此做者未打出來
詳情可轉至zsjzliziyang的文章blog

Solution 2

這解法我仍是打了的[小慶幸]ip

Mentality

因爲點的度數只有1,2兩種,圖的組成必定是若干條鏈與若干個環,設度數爲1,2點的個數分別爲c1,c2。get

一條鏈兩端有兩個度數爲1的點,因而鏈的條數k肯定了k=c1/2(c1爲奇數則無解)。input

首先考慮c1個點兩兩匹配的方案數,考慮將c1個點放到左右兩邊對齊的方案,即A(c1,c1/2),而後對齊的一對點互相交換是同一種方案,即每種方案算重複2^(c1/2)次。綜上c1個點兩兩匹配方案數爲A(c1,c1/2)/(2^(c1/2))。it

而後考慮c2個點怎麼分配。枚舉x表示分配x個點組成鏈,c2-x個點組成環,先乘上係數C(c2,x)。io

設f[i]表示i個點插進k條鏈的方案,只需考慮i有幾個位置可插。以前已經插入i-1個點,所以有i-1+k個位置可插,遞推式:class

f[0]=1
f[i]=f[i-1]*(i-1+k)

設g[i]表示i個點組成若干環的方案,分兩種狀況:1.i與前面兩點新建三元環 2.i插入到以前某個環 遞推式:

g[0]=1
g[i]=g[i-3]C(i-1,2)+g[i-1](i-1)

最終答案即爲:
sigma(f[x]g[c2-x]C(c2,x))

預處理階乘逆元,組合數能夠O(1)算出,f,g能夠O(n)遞推,答案也可O(n)計算,總複雜度O(n)

Code

#include <cstdio>
#include <algorithm>
#define MO 998244353
#define N 2001
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
int i,n,a;
long long ans,t,tot,p[3],f[N],g[N],inv[5001],inc[5001];
long long ksm(long long x,int y)
{
    long long sum=1;
    while (y)
    {
        if (y&1)sum=sum*x%MO;
        x=x*x%MO;
        y>>=1;
    }
    return sum;
}
long long C(long long x,long long y)
{
	return inv[x]*inc[y]%MO*inc[x-y]%MO;
}
int main()
{
    open("graph");
    inv[1]=inv[0]=1;
    for (i=2;i<=5000;i++)  
        inv[i]=inv[i-1]*i%MO;
    inc[5000]=ksm(inv[5000],MO-2);
    for (i=4999;i>=0;i--)
        inc[i]=inc[i+1]*(i+1)%MO;
    scanf("%d",&n);
    for (i=1;i<=n;i++)
    {
        scanf("%d",&a);
        p[a]++;
    }
    tot=p[1]/2;
    if (p[1]%2!=0) 
    {
        printf("0");
        return 0;
    }
    t=inc[tot]*inv[p[1]]%MO*ksm(ksm(2,tot),MO-2)%MO;
    f[0]=g[0]=1;
    for (i=1;i<=p[2];i++)
    {
        f[i]=f[i-1]*(i-1+tot)%MO;
        g[i]=g[i-1]*(i-1)%MO;
        if (i>=3) g[i]=(g[i]+g[i-3]*C(i-1,2))%MO;
    }
    for (i=0;i<=p[2];i++)
        ans=(ans+f[i]*g[p[2]-i]%MO*C(p[2],i)%MO)%MO;
    printf("%lld",t*ans%MO);
    return 0;
}
相關文章
相關標籤/搜索