BZOJ2560 串珠子

题意

铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。

现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。

铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。

\(n \leq 16\)

分析

参考SFN1036的题解。

题意即:给出一个大小为n的无向图,边有边权。定义一个子图的权值为所有边权的乘积,问所有使全部n个点连通的子图的权值和为多少。

\(f(s)\)表示使\(s\)中的点连通且不管其余点的所有子图的权值和为多少,\(g(s)\)表示只看集合\(s\)中的点,所有子图的权值和是多少。

显然\(g(s)\)可以预处理,考虑如何求\(f(s)\)

直接求显然不好求,考虑用所有方案减去不合法的方案。按照套路,我们可以枚举编号最小的点所在的连通块,设为集合\(t\),那么

\[f(s)=g(s)-\sum_{t}f(t) \cdot g(s-t) \]

时间复杂度\(O(n2^n + 3^n)\)

关于基准点的问题

不重

直接枚举子集会有重复的情况。具体而言重复的是“f枚举到了 原来的g的子集 ,然后g包含 原来的f 和 现在的f在原来的g中的补集 ”。

然后不找基准点的话就会重复,设想对于一种连通块个数确定的情况,会重复连通块个数那么多次。

然后确定了一个基准点以后,f就不可能再是g的子集了,所以不会重复。

不漏

由于不连通一定可以看成包含基准点的连通块与其他连通块不连通,所以不会遗漏。

无另解

每种情况的每个连通块都会作为t被计算一次,所以是连通块个数次。

但是所说的每种情况并未在状态中体现,所以虽然单个情况的确重复了连通块个数次,但是总体考虑是无法确定具体重复了多少次的。

也就是说没法通过不确定基准点计算,然后减去重复的情况这么做

%出题人

sto 出题人 orz

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
	rg T data=0;
	rg int w=1;
	rg char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-')
			w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		data=data*10+ch-'0';
		ch=getchar();
	}
	return data*w;
}
template<class T>T read(T&x)
{
	return x=read<T>();
}
using namespace std;
typedef long long ll;

co int MAXN=16,mod=1e9+7;
int n,c[MAXN][MAXN];

int log2(int x)
{
	int res=-1;
	while(x)
	{
		x>>=1;
		++res;
	}
	return res;
}

int add(int x,int y)
{
	x+=y;
	return x>=mod?x-mod:x;
}

int sub(int x,int y)
{
	x-=y;
	return x<0?x+mod:x;
}

int mul(int x,int y)
{
	return (ll)x*y%mod;
}

int f[1<<MAXN],g[1<<MAXN];

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n);
	for(int i=0;i<n;++i)
		for(int j=0;j<n;++j)
			read(c[i][j]);
	g[0]=1;
	for(int i=1;i<(1<<n);++i)
	{
		int x=log2(i&-i);
//		cerr<<"x="<<x<<endl;
		g[i]=g[i-(1<<x)];
		for(int j=0;j<n;++j)
			if(i&(1<<j))
				g[i]=mul(g[i],c[x][j]+1);
//		cerr<<i<<" g="<<g[i]<<endl;
	}
	g[0]=0;
	for(int i=1;i<(1<<n);++i)
	{
		f[i]=g[i];
		int x=i&-i,s=i-x;
		for(int j=s;j>=0;j=!j?-1:(j-1)&s)
			f[i]=sub(f[i],mul(f[x+j],g[s-j]));
//		cerr<<i<<" f="<<f[i]<<endl;
	}
	printf("%d\n",f[(1<<n)-1]);
	return 0;
}

posted on 2018-11-27 11:16  autoint  阅读(96)  评论(0编辑  收藏  举报

导航