【校内模拟】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;}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章