• 串珠子[BZOJ2560]


    题目描述

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

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

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

    输入格式

    输入第一行包含一个正整数 (n),表示珠子的个数。接下来 (n) 行,每行包含 (n) 个非负整数,用空格隔开。这 (n) 行中,第 (i) 行第 (j) 个数为 (c_{i,j})

    输出格式

    输出一行一个整数,为连接方案数对 (1000000007) 取模的结果。

    (nle 20)

    题解

    状压DP

    首先我们设(f[S])表示:(S)中包含的珠子两两之间任意连边(可以不连)的方案数

    显然,(f[S]=Pi_{iin S,jin S} (c[i][j]+1))

    这个可以在(O(2^nn^2))的时间内预处理

    但是这样求出的方案,不一定保证每个方案都是一个连通图

    所以我们要再进行一次DP

    (g[S])表示:(S)中包含的珠子两两之间任意连边(可以不连),且构成一个连通块的方案数

    初始时我们先把每个(g[S])赋值成(f[S]),然后再删除掉不合法的部分

    我们考虑(1)这颗珠子,在一个不合法的方案中,一定有部分珠子和(1)不在同一个连通块内

    所以对于每个(g[S]),我们枚举(S)的一个不含珠子(1)的子集(S_2),表示(S_2)中的那些珠子和(1)不在同一个连通块内

    这样的方案数有多少?首先,(S_2)内的珠子不一定要形成一整个连通块,所以是(f[S_2])

    其次,(S-S_2)即与(1)连通的珠子一定在一个连通块内,所以是(g[S-S_2])

    所以不合法的方案有(f[S_2]*g[S-S_2])种,从(g[S])中减去

    所以我们可以得到:(g[S]=f[S]-sum f[S_2]*g[S-S_2]) ((S_2)(S)的所有不含珠子(1)的子集)

    答案即为(g[2^n-1])

    p.s. 我们发现所有的(S-S_2)都是含有珠子1的,所以为了降低复杂度,我们可以不去求那些不含珠子(1)(g[S])

    时间复杂度为枚举子集复杂度(O(3^n)) 但是大大小于这个上界

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template<typename T>
    inline void read(T &num) {
    	T x = 0, ff = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') ff = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    	num = x * ff; 
    }
    
    const ll mod = 1000000007;
    int n;
    ll a[30][30], f[1050005], g[1050005]; 
    //f:可以不联通  g:必须联通 
    
    inline ll fpow(ll x, ll t) {
    	ll ret = 1;
    	for (; t; t >>= 1, x = x * x % mod) if (t & 1) ret = ret * x % mod;
    	return ret;
    }
    
    int main() {
    	read(n);
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= n; j++) {
    			read(a[i][j]);
    		}
    	}
    	for (int s = 0; s <= (1 << n) - 1; s++) {
    		f[s] = 1;
    		for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) {
    			if (((s>>(i-1))&1) && ((s>>(j-1))&1)) {
    				f[s] = f[s] * (a[i][j] + 1) % mod;
    			}
    		} 
    	}
    	memcpy(g, f, sizeof(g));
    	for (int s = 1; s <= (1 << n) - 1; s++) {
    		if (!(s & 1)) continue;
    		for (int s2 = (s - 1) & s; s2; s2 = (s2 - 1) & s) {
    			if (!(s2 & 1)) continue;
    			g[s] = (g[s] - g[s2] * f[s-s2] % mod + mod) % mod;
    		}
    	}
    	printf("%lld
    ", g[(1<<n)-1]);
    	return 0;
    }
    
  • 相关阅读:
    从.Net迁移到.Net Core时,需要注意的兼容性变更
    P/Invoke各种总结(五、在C#中使用指针类型)
    P/Invoke各种总结(四、平台调用时的数据类型对应)
    解决Visual Studio 2017/2019 "const char *"类型的值不能用于初始化"char *"类型的实体的问题
    .Net Framework、.Net Core和.Net Standard的区别
    C#代码分析工具Style Cop使用
    《代码不朽:编写可维护软件的10大要则(C#版)》读书笔记
    Visual C++中宽字节与多字节字符互相转换的方法
    浮动和定位
    关于如何设置图片大小和图片形状
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM82.html
Copyright © 2020-2023  润新知