1. 程式人生 > >【XSY1295】calc n個點n條邊無向連通圖計數 prufer序列

【XSY1295】calc n個點n條邊無向連通圖計數 prufer序列

ring pre end ctime 節點 splay 按順序 sin algorithm

題目大意

  求\(n\)個點\(n\)條邊的無向連通圖的個數

  \(n\leq 5000\)

題解

  顯然是一個環上有很多外向樹。

  首先有一個東西:\(n\)個點選\(k\)個點作為樹的根的生成森林個數為:
\[ \binom{n}{k}\times n^{n-k-1}\times k \]
  前面\(\binom{n}{k}\)是這些根的選編號的方案數,後面是prufer序列得到的:前面\(n-k-1\)個數可以是\(1\)~\(n\),第\(n-k\)個數是\(1\)~\(k\)

  我的理解是:每個序列決定了一部分點作為"葉子節點",剩下的每個點按順序選一個編號最小的"葉子節點"作為這個點的兒子(選編號最小的是因為1.如果一個點可以選多個兒子就不會重復計數;2.兩個數的先後順序不同,那麽選的兒子也不同,會讓先後順序成為影響因素),然後如果這個點不能再選兒子,那麽這個點就會成為"葉子節點"。選了\(n-k-1\)

個點後會剩下\(k\)個根和一個不是根的點,然後\(k\)個根中的一個點連向剩下這個點。

  最後\(k\)個點的環的排列方式有\(\frac{(k-1)!}{2}\)。你可以選編號最小的點為"根",剩下\(k-1\)個點每次選一個點連向上一個點,最後一個點再連向第一個點。因為環可以翻轉,所以方案數要除以\(2\)。你也可以認為是先生成一個排列,然後旋轉這個環(除以\(k\)),然後翻轉這個環(除以\(2\))。

  最終的式子是
\[ \begin{align} &~~~~~\sum_{k=3}^{n}\binom{n}{k}\times n^{n-k-1}\times k\times \frac{(k-1)!}{2}\&=\sum_{k=3}^{n}\frac{n!\times n^{n-k-1}\times k\times (k-1)!}{k!\times (n-k)!\times 2}\&=\sum_{k=3}^{n}\frac{n!n^{n-k-1}}{2(n-k)!} \end{align} \]


  寫個高精度什麽的亂搞一下就可以了。

  時間復雜度:\(O(n^2)\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int p=10000;
struct bign
{
    int a[5010];
    bign()
    {
        memset(a,0
,sizeof a); } int &operator [](int x) { return a[x]; } bign &operator *=(int v) { int i,s,g=0; bign &a=*this; for(i=1;i<=5000;i++) { s=g+a[i]*v; g=s/p; a[i]=s%p; } return a; } bign &operator /=(int v) { int i,s,g=0; bign &a=*this; for(i=5000;i>=1;i--) { s=g*p+a[i]; a[i]=s/v; g=s%v; } g=0; for(i=1;i<=5000;i++) { s=g+a[i]; a[i]=s%p; g=s/p; } return a; } bign &operator +=(bign &b) { int i,s,g=0; bign &a=*this; for(i=1;i<=5000;i++) { s=a[i]+b[i]+g; a[i]=s%p; g=s/p; } return a; } }; bign a,ans; int main() { int n; scanf("%d",&n); int i; a[1]=1; for(i=2;i<=n-1;i++) a*=i; ans+=a; for(i=n-1;i>=3;i--) { a*=n; a/=n-i; ans+=a; } ans/=2; for(i=5000;!ans[i];i--); printf("%d",ans[i]); for(i--;i;i--) printf("%04d",ans[i]); printf("\n"); return 0; }

【XSY1295】calc n個點n條邊無向連通圖計數 prufer序列