【XSY1295】calc n个点n条边无向连通图计数 prufer序列

题目大意

  求\(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;
}

原文地址:https://www.cnblogs.com/ywwyww/p/8511316.html

时间: 2024-09-29 08:23:29

【XSY1295】calc n个点n条边无向连通图计数 prufer序列的相关文章

用HTML、CSS、JS制作圆形进度条(无动画效果)

逻辑 1.首先有一个圆:蓝色的纯净的圆,效果: 2.再来两个半圆,左边一个,右边一个将此蓝色的圆盖住,效果: 此时将右半圆旋转60°,就会漏出底圆,效果: 然后我们再用一个比底圆小的圆去覆盖这个大圆就可以出进度条效果了 代码: <style>     /*支持IE9及以上*/    .circle-bar {margin: 20px; font-size:200px; width: 1em; height: 1em; position: relative;  background-color:

第30条:以ARC简化引用计数

本条要点:(作者总结) 引用计数这个概念相当容易理解.需要执行保留与释放操作的地方也很容易就能看出来.所以 Clang 编译器项目带有一个 "静态分析器"(static analyzer).用于指明程序里引用计数出问题的地方.举个例子,假设下面这段代码采用手工方式管理引用计数: 1 if ([self shouldLogMessage]) { 2 3 NSString *message = [[NSString alloc] initWithFormat:@"I am obj

DJANGO和UIKIT结合,作一个有进度条的无刷新上传功能

以前作的上传,在糙了,所以在用户体验上改进一下. 同时,结合DJANGO作定位上传. 这其中分两步进行,第一次上传到TMP目录下, 第二次,将TMP下的文件转移到标准目录下. form.py file_path = forms.CharField( required=True, label=u"上传文件", widget=forms.TextInput( attrs={ 'rows': 2, 'class': 'uk-width-1-2', } ), ) upload.html {#

图的遍历——A1013Battle over cities(25) 求需要添加多少条边才能构成连通图转化为找连通分量(可由DFS 和 并查集来找连通分量)

#include <bits/stdc++.h> #include <stdio.h> #include <stdlib.h> #include <queue> using namespace std; const int N = 1111; vector<int> G[N];//邻接表 bool vis[N];//标记顶点i是否被访问 int currentPoint;//当前需要删除的顶点编号 void dfs(int v){ if(v ==

图的计数

无标号计数与带标号计数 无标号就是在观测上不考虑个体的差异, 带标号就是在观测上考虑个体的差异. 对于无标号计数, 我们可以按照某种关键值, 给所有的元素进行定序, 所有无标号计数等价于定序计数. 对于带标号计数, 我们可以任意设置关键值, 所以带标号计数等价于不定序计数. 最常见的例子就是组合与排列, 它们还满足一组特殊的关系: 组合数 * n! = 排列数. 有向图 / 无向图 带标号无向图的 度数的K次方 的和 分析 $ans = n \sum_{d = 0} ^ {n - 1} \bin

最短路径条数问题

最短路径条数问题: 给定如图所示的无向连通图,假定图中所有边的权值都为1,显然,从源点A到终点T的最短路径有多条,求不同的最短路径的数目. 如图: 程序实现: 1 #include <iostream> 2 #include <queue> 3 #include <cstring> 4 using namespace std; 5 6 const int N = 16; 7 8 int CalcShortestPathNum(int G[N][N]){ 9 int st

poj 3177 求至少添加多少条边可以成为边-双连通图(有重边)

[题意]:给出一张无向连通图,求添加多少条边可以成为边-双连通图 [思路]:同3352 一样,求出边-双连通分量,缩点就成了一棵树,求这棵树里的出度为1 的点num  结果是(num-1)/2; 但是!!  这里和3352 哟一点不一样就是这里有重边,当有重边的时候,不同low值的两点可能属于同一个边-双连通分量 所以在构图的时候要注意把重边去掉! 1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h>

【2016常州一中夏令营Day5】

小 W 拼图[问题描述]小 W 和小 M 一起玩拼图游戏啦~小 M 给小 M 一张 N 个点的图,有 M 条可选无向边,每条边有一个甜蜜值,小 W 要选K 条边,使得任意两点间最多有一条路径,并且选择的 K 条边甜蜜值之和最大.[输入格式]第一行三个正整数 N,M,K.接下来 M 行,每行三个正整数 A,B,C,表示 A.B 两点间有一条甜蜜值为 C 的无向边.[输出格式]一行输出最大甜蜜值之和.[输入输出样例]carpet.in 5 4 31 2 101 3 92 3 74 5 3 carpe

[蒟蒻修炼计划][谜之吐槽]常州集训day5

T1 Description 小W和小M一起玩拼图游戏啦~ 小M给小M一张N个点的图,有M条可选无向边,每条边有一个甜蜜值,小W要选K条边,使得任意两点间最多有一条路径,并且选择的K条边甜蜜值之和最大. Input 第一行三个正整数N,M,K. 接下来M行,每行三个正整数A,B,C表示A.B两点间有一条甜蜜值为C的无向边. Output 一行输出最大甜蜜值之和. Sample Input 5 4 3 1 2 10 1 3 9 2 3 7 4 5 3 Sample Output 22 HINT N