1. 程式人生 > 其它 >#dp#洛谷 3244 [HNOI2015]落憶楓音

#dp#洛谷 3244 [HNOI2015]落憶楓音

題目


分析

每個有入度的點可以選擇任意一個父節點組成一棵樹,那麼原來的答案就是 \(\prod_{i=2}^ndeg[i]\)

現在多了一條邊,如果邊的終點是1或者它是一個自環那麼可以不用管這條邊。

然後先把這條邊加上再減去不合法的方案,可以發現不合法的方案就是選出任意一個環,它貢獻的方案數就是 \(\Large \frac{\prod_{i=2}^ndeg[i]}{\prod_{i\in it}deg[i]}\)

那麼設 \(dp[y]\) 表示 \(y\)\(x\) 的環的方案數,\(dp[y]=\frac{1}{deg[y]}\sum{dp[y']}\)

\(dp[x]=\prod_{i=2}^ndeg[i]\)

,最後不合法的方案就是 \(dp[y]\)


程式碼

#include <cstdio>
#include <cctype>
using namespace std;
const int N=100011,mod=1000000007;
struct node{int y,next;}e[N<<1];
int dp[N],ieg[N],ans=1,v[N],sum=1,as[N],deg[N],n,m,X,Y;
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
int ksm(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=1ll*x*x%mod)
	    if (y&1) ans=1ll*ans*x%mod;
	return ans; 
}
void Mo(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
void dfs(int x){
	if (v[x]) return;
	v[x]=1;
	for (int i=as[x];i;i=e[i].next)
		dfs(e[i].y),Mo(dp[x],dp[e[i].y]);
	dp[x]=1ll*dp[x]*ieg[x]%mod;
}
int main(){
	n=iut(),m=iut(),X=iut(),Y=iut();
	for (int i=1;i<=m;++i){
		int x=iut(),y=iut();
		e[i]=(node){y,as[x]},as[x]=i;
		++deg[y];
	}
	if (X==Y||Y==1){
		for (int i=2;i<=n;++i) ans=1ll*ans*deg[i]%mod;
		return !printf("%d",ans);
	}
	for (int i=2;i<=n;++i){
		sum=1ll*sum*deg[i]%mod;
		if (i!=Y) ans=1ll*ans*deg[i]%mod;
	}
	ieg[1]=1,Mo(ans,sum);
	for (int i=2;i<=n;++i) ieg[i]=1ll*ieg[i-1]*deg[i]%mod;
	ieg[n]=ksm(ieg[n],mod-2);
	for (int i=n,now=ieg[n];i>1;--i){
		ieg[i]=1ll*ieg[i-1]*now%mod;
		now=1ll*now*deg[i]%mod;
	}
	dp[X]=sum,dfs(Y),Mo(ans,mod-dp[Y]);
	return !printf("%d",ans);
}