考虑将点分为 $A,B$ 两类。其中 $[x\in A]\iff\exists_{y\neq x},y|x$。

那么我们删去的点只可能在 $B$ 类中,且当前 $x\in B$ 可删当且仅当存在 $y\in B,z\in A$ 使得 $z|x\land z|y$。

那么对于 $z\in A,x\in B,z|x$,连边 $(z,x)$。这样得到一个二分图,每次删除操作相当于选择某个 $z\in A$ 且 $z$ 的度数 $\geq 2$,然后删去某个和 $z$ 相邻的点。

对二分图中每个连通块单独考虑,假设该连通块 $B$ 类有 $m$ 个点,那么发现答案能达到上界 $m-1$:以某个 $B$ 类点开始跑一棵生成树,然后从叶子往根删即可。

现在考虑统计方案数。只需统计单个连通块内的方案数,然后连通块间乘个组合数即可。

发现连通块内能删到上界的等价条件是:每次删完某个 $B$ 类点后,剩下的 $B$ 类点仍然是连通的。进一步地,若我们枚举最后剩下的是哪个 $B$ 类点 $lst$,我们只需保证当前所有未删的 $B$ 类点都与 $lst$ 连通即可。

数据范围提示我们可能是指数级做法。那么先挖掘到一条可能有用的性质:$A$ 类点至多 $20$ 个,因为 $>20$ 的 $A$ 类点至多有 $1$ 条邻边,那么它是没用的。

进一步地,由于 $x,2x$ 不可能同时成为 $A$ 类点,所以现在有用的 $A$ 类点至多 $10$ 个。同时这也是紧界:考虑 $A$ 类点为 $11,12,\cdots,20$。

把删点变成加点,虽然是等价的,但是会直观很多,且助于思考。

考虑 DP。发现我们可以只关注 $A$ 类点的添加过程(具体来说,是 $A$ 类点在 $B$ 类点添加序列的什么位置变化了,且变化是什么),而通过组合数算出 $B$ 类点的添加方案数。可以做到时间复杂度 $O(n2^{n/6})$。

#include<bits/stdc++.h>

#define N 65

using namespace std;

namespace modular
{
	const int mod=1000000007;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
	inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
	inline void Mul(int &x,int y){x=1ll*x*y%mod;}
	inline int poww(int a,int b){int ans=1;for(;b;Mul(a,a),b>>=1)if(b&1)Mul(ans,a);return ans;}
}using namespace modular;

int n,a[N],fa[N];
int fac[N],ifac[N];
bool AB[N],vis[N];

int find(int x)
{
	return x==fa[x]?x:(fa[x]=find(fa[x]));
}

pair<int,int> solve(int rt)
{
	static int f[1050],w[1050],from[N];
	vector<int> A,B;
	for(int i=1;i<=n;i++)
		if(find(i)==rt) (AB[i]?B:A).push_back(a[i]),vis[i]=1;
	while(!A.empty()&&A.back()>20) A.pop_back();
	int maxn=(1<<A.size())-1;
	memset(f,0,sizeof(int)*(maxn+1));
	for(int i=0;i<B.size();i++)
	{
		from[i]=0;
		for(int j=0;j<A.size();j++)
			if(!(B[i]%A[j])) from[i]|=(1<<j);
	}
	for(int S=0;S<=maxn;S++)
	{
		w[S]=0;
		for(int j=0;j<B.size();j++)
			if((from[j]&S)==from[j]) w[S]++;
	}
	for(int i=0;i<B.size();i++)
		Add(f[from[i]],mul(fac[B.size()-1],ifac[B.size()-w[from[i]]]));//C(B-1,w[from[i]]-1)*fac[w[from[i]-1]]
	for(int S=0;S<maxn;S++)
	{
		if(!f[S]) continue;
		for(int j=0;j<B.size();j++)
		{
			if((S&from[j])&&((S&from[j])!=from[j]))
			{
				int T=S|from[j];
				Add(f[T],mul(mul(fac[B.size()-w[S]-1],ifac[B.size()-w[T]]),f[S]));
				//C(x,y)*fac[y]
			}
		}
	}
	return make_pair(B.size()-1,f[maxn]);
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<i;j++)
			if(!(a[i]%a[j])){AB[i]=1;continue;}
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=n;i++)
		if(!AB[i]) for(int j=i+1;j<=n;j++)
			if(!(a[j]%a[i])) fa[find(i)]=find(j);
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
	ifac[n]=poww(fac[n],mod-2);
	for(int i=n;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
	int ans=1,s=0;
	for(int i=1;i<=n;i++)
	{
		if(AB[i]&&find(i)==i&&!vis[i])
		{
			auto pr=solve(i);
			Mul(ans,mul(pr.second,ifac[pr.first]));
			s+=pr.first;
		}
	}
	Mul(ans,fac[s]);
	printf("%d\n",ans);
	return 0;
}

原文地址:http://www.cnblogs.com/ez-lcw/p/16845549.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性