CF1109D Sasha and Interesting Fact from Graph Theory


CF1109D Sasha and Interesting Fact from Graph Theory

這個 \(D\) 題比賽切掉的人基本上是 \(C\) 題的 \(5,6\) 倍...果然數學計數問題比數據結構更受歡迎...

  • 以下大致翻譯自官方題解.
  • 枚舉 \(a\to b\) 路徑上邊的數目,記為 \(edges\) .
  • 先來考慮給定的兩個點路徑上的 \(edges-1\) 個點(不含 \(a,b\) )和 \(edge\) 條邊.
    • 節點有\(edges-1\)個,順序不同則最后的樹不同,所以方案數為 \(A(n-2,edges-1)\) .
    • 邊有 \(edges\) 條,邊權 \(v\) 需滿足\(v \in \mathbb{N_+},v_1+v_2+...+v_{edges-1}+v_{edges}=m\).用隔板法可知方案數,即解的組數為 \(C(m-1,edges-1)\).
  • 再來考慮其它的 \(n-edges-1\) 個點和 \(n-edges-1\) 條邊.
    • 由於其它邊的邊權顯然不影響合法性,可以隨意賦 \([1,m]\) 內的整數值,方案數為 \(m^{n-edges-1}\).
    • 剩下的點我們需要使它們形成一個森林,並將每顆樹掛在 \(a\to b\)\(edges+1\) 個點上.這等價於所有的 \(n\) 個點形成一個 \(edges+1\) 顆樹的森林,那 \(edges+1\) 個點都屬於不同的樹,然后將這 \(edges+1\) 個點連接起來.根據廣義\(Cayley\)定理,方案數為 \((edges+1) \cdot n^{n-edges-2}\) .

廣義 \(Cayley\) 定理:

\(n\) 個標號節點形成一個有 \(k\) 顆樹的森林,使得給定的 \(k\) 個點沒有兩個點屬於同一顆樹的方案數為\(k\cdot n^{n-k-1}.\)

證明可以用歸納法,對 \(n\) 歸納,枚舉節點 \(1\) 的鄰居即可得遞推式,進而得出證明.

  • 那么我們就得到了在 \(edges\) 確定的情況下的答案:

\[f(edges)=A(n-2,edges-1) \cdot C(m-1,edges-1)\cdot m^{n-edges-1} \cdot (edges+1) \cdot n^{n-edges-2}. \]

  • 線性預處理 \(m,n​\) 的冪,階乘及階乘逆元,枚舉 \(edges​\) 統計答案,時間復雜度為 \(O(n+m)\).
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			pos=0;
	for(;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
const int P=1e9+7;
inline int add(int a,int b)
{
	return (a + b) % P;
}
inline int mul(int a,int b)
{
	return 1LL * a * b % P;
}
int fpow(int a,int b)
{
	int res=1;
	while(b)
		{
			if(b&1)
				res=mul(res,a);
			a=mul(a,a);
			b>>=1;
		}
	return res;
}
const int MAXN=1e6+10;
int fac[MAXN],invfac[MAXN],mpow[MAXN],npow[MAXN];
void init(int n,int m)
{
	int mx=max(n,m);
	fac[0]=1;
	for(int i=1;i<=mx;++i)
		fac[i]=mul(fac[i-1],i);
	invfac[mx]=fpow(fac[mx],P-2);
	for(int i=mx-1;i>=0;--i)
		invfac[i]=mul(invfac[i+1],i+1);
	mpow[0]=npow[0]=1;
	for(int i=1;i<=n;++i)
		mpow[i]=mul(mpow[i-1],m);
	for(int i=1;i<=n;++i)
		npow[i]=mul(npow[i-1],n);
}
int A(int n,int m)
{
	if(n<m || n<0 || m<0)
		return 0;
	return mul(fac[n],invfac[n-m]);
}
int C(int n,int m)
{
	if(n<m || n<0 || m<0)
		return 0;
	return mul(fac[n],mul(invfac[n-m],invfac[m]));
}
int main()
{
	int n=read(),m=read();
	int a=read(),b=read();
	init(n,m);
	int ans=0;
	for(int edges=1;edges<n;++edges)
		{
			int tmp=mul(A(n-2,edges-1),C(m-1,edges-1));
			tmp=mul(tmp,mpow[n-edges-1]);
			tmp=mul(tmp,edges==n-1?1:mul(edges+1,npow[n-edges-2]));
			ans=add(ans,tmp);
		}
	printf("%d\n",ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM