• [CF156D] Clues


    题目大意

    给定一个 n 个点 m 条边的带标号无向图,它有 k 个连通块,求添加 k-1 条边使得整个图连通的方案数,答案对 p 取模。

    解析

    考虑将k个连通块看作是k个点。我们枚举每个连通块的度数,由Prufer序列的结论,我们可以得到总方案数为:

    [frac{(k-2)!}{prod_{i=1}^{k}(d_i-1)!}prod_{i=1}^{k}s_i^{d_i} ]

    其中(d)满足(d_i>0)(sum_{i=1}^kd_i=2k-2)(s_i)是第(i)个连通块的大小。

    关于二项式定理有一个推论:

    [(x_1+x_2+...+x_m)^n=frac{n!}{prod_{i=1}^{m}c_i!}prod_{i=1}^m x_i^{c_i} (sum_{i=1}^mc_i=n) ]

    证明比较显然,依次使用一般二项式定理即可。

    因此,最后的答案为((s_1+s_2+...+s_k)^{k-2}prod_{i=1}^k s_i)

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 100002
    using namespace std;
    int head[N],ver[N*2],nxt[N*2],l;
    int n,m,p,k,i,s[N];
    bool vis[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y)
    {
    	l++;
    	ver[l]=y;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void dfs(int x,int id)
    {
    	vis[x]=1;s[id]++;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(!vis[y]) dfs(y,id);
    	}
    }
    int poww(int a,int b)
    {
    	int ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%p;
    		base=base*base%p;
    		b>>=1;
    	}
    	return ans;
    }
    signed main()
    {
    	n=read();m=read();p=read();
    	for(i=1;i<=m;i++){
    		int u=read(),v=read();
    		insert(u,v);insert(v,u);
    	}
    	for(i=1;i<=n;i++){
    		if(!vis[i]) k++,dfs(i,k);
    	}
    	if(k==1){
    		printf("%lld
    ",1%p);
    		return 0;
    	}
    	int ans=poww(n,k-2);
    	for(i=1;i<=k;i++) ans=ans*s[i]%p;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    点击子窗体给父窗体上的对象赋值
    框架使用及规范参考
    像Google日历一样的日程管理
    TreeView 和 Menu 的用法
    甘特图-svg版 支持客户端事件
    js获取DropDownList的选择项
    GridView,Repeater分页控件:WebPager(开源)
    TextBox 禁止客户端输入 前台通过JS赋值 并在后台获取
    对象实体 参考标准
    以编程方式控制ScriptManager
  • 原文地址:https://www.cnblogs.com/LSlzf/p/13499173.html
Copyright © 2020-2023  润新知