【校內模擬】Fygon 2.0(狀壓DP)

原題傳送門

目前計蒜客上面AC數量還是0,我也懶得交,估計CF Gym裏面也該有這道,懶得找了。


題解:

今天的簽到題。

把for循環換個思路考慮:

	for var in range ( l , r ):

冷靜一想不難發現等價於:

	for var in range ( 1 , n ):
		if(l <= var && var <= r)

於是實際上要求的就是有多少變量取值滿足題意給的不等式鏈。

首先建立有向邊,然後縮點,由於圖太小了我就懶得寫tarjan,直接用Floyd,顯然同一個SCC裏面的變量取值要一樣,不同的構成一個DAG。

複雜度中 nn 的次數顯然就是 DAGDAG 的點數。

考慮一下係數表示的東西是什麼,這裏比較抽象,表示的應該是所有可能的取值情況佔了所有情況的幾分之幾。

漸進意義下強行讓所有SCC不同不會改變答案,於是係數就是 /sccCnt!合法拓撲序個數/sccCnt! ,考慮所有點的大小關係還是挺顯然的。

求拓撲序個數顯然直接狀壓DP即可。


代碼:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=21;

int n;
bool g[N][N];
int rt[N],bel[N];
ll dp[(1<<N)|7];
int ct=0,mp[1000];

int gid(int x){
	return (mp[x]?mp[x]:mp[x]=++ct)-1;
}

void Main(){
	std::ios::sync_with_stdio(false);
	int m;std::cin>>m;
	for(int re i=1;i<m;++i){
		std::string tmp,v,l,r;
		std::cin>>tmp>>v>>tmp>>l>>r;
		int a=gid(v[0]),b=l[6],c=r[0];
		if(b!='1')g[gid(b)][a]=true;
		if(c!='n')g[a][gid(c)]=true;
	}
	for(int re k=0;k<ct;++k)
		for(int re i=0;i<ct;++i)
			for(int re j=0;j<ct;++j)
				g[i][j]|=g[i][k]&g[k][j];
	for(int re i=0;i<ct;++i){
		bool tos=false;
		for(int re j=0;j<i&&!tos;++j)
			tos|=g[i][j]&&g[j][i];
		if(!tos)rt[n++]=i;
	}
	for(int re i=0;i<n;++i)
		for(int re j=0;j<n;++j){
			if(g[rt[i]][rt[j]]&&i!=j)
				bel[i]|=1<<j;
		}
	dp[0]=1;
	for(int re s=0;s<(1<<n);++s)
		for(int re i=0;i<n;++i)
			if(!(s&(1<<i))&&(bel[i]|s)==s)
				dp[s|(1<<i)]+=dp[s];
	ll up=dp[(1<<n)-1],dn=1;
	for(int re i=1;i<=n;++i)dn*=i;
	ll g=std::__gcd(up,dn);
	cout<<n<<" "<<up/g<<"/"<<dn/g<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("fygon.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("fygon.in","r",stdin);
	freopen("fygon.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章