zoukankan      html  css  js  c++  java
  • [tsinsen_A1278]串珠子

    [tsinsen_A1278]串珠子

    试题描述

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

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

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

    输入

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

    输出

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

    输入示例

    3
    0 2 3
    2 0 4
    3 4 0
    

    输出示例

    50
    

    数据规模及约定

    对于 (100\%) 的数据,(n) 为正整数,所有的 (c_{i,j}) 为非负整数且不超过 (1000000007)。保证 (c_{i,j}=c_{j,i})。每组数据的 (n) 值如下表所示。

    编号12345678910
    $n$$8$$9$$9$$10$$11$$12$$13$$14$$15$$16$

    题解

    一道挺经典的状压 dp,这题有一些计数技巧。

    状态很简单,就是令 (f(s)) 表示集合 (s) 内所有点处于一个连通分量内的方案数,关键是怎么转移。

    为了让计算的方案不重不漏,我们需要设计一个方案使得每次转移都是唯一的,不会被重复转移,同时也能够让转移覆盖到所有情况。

    这题就是找到集合 (s) 中的前两个元素(或者第一个和最后一个元素也行,需要保证每次都选的是特定的两个元素),令第一个为 (t_1),第二个为 (t_2),然后我们要枚举第一次拆解(这次拆解后的两个集合都通过 (t_1) 这个点相连,即 (t_1)(s) 连通分量的割顶),并且由于这个拆解是没有顺序的,所以我们要保证每次拆解都需要将 (t_1)(t_2) 分开。于是枚举 (s) 的子集 (s'),使得 (t_2 in s')(t_1 in s - s'),然后将 (s) 分成 (s')(s - s') 两个集合,最后转移就是 (f(s) = f(s') cdot f(s-s') cdot prod_{i in s'} {c_{t_1, i}})

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s); i <= (t); i++)
    #define dwn(i, s, t) for(int i = (s); i >= (t); i--)
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *Tail;
    inline char Getchar() {
    	if(Head == Tail) {
    		int l = fread(buffer, 1, BufferSize, stdin);
    		Tail = (Head = buffer) + l;
    	}
    	return *Head++;
    }
    int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 20
    #define maxs 65536
    #define MOD 1000000007
    #define LL long long
    
    int n, c[maxn][maxn], Log[maxs], f[maxs], mul[maxn][maxs];
    
    int main() {
    	n = read();
    	rep(i, 0, n - 1) rep(j, 0, n - 1) c[i][j] = read();
    	
    	int all = (1 << n) - 1;
    	Log[1] = 0;
    	rep(i, 2, all + 1) Log[i] = Log[i>>1] + 1;
    	rep(i, 0, n - 1) {
    		mul[i][0] = 1;
    		rep(s, 1, all) mul[i][s] = (LL)mul[i][s&~(s&-s)] * (c[i][Log[s&-s]] + 1) % MOD;
    	}
    	rep(s, 1, all) {
    		int cnt = 0, t1 = -1, t2 = -1;
    		rep(i, 0, n - 1) if(s >> i & 1) {
    			cnt++;
    			if(t1 < 0) t1 = i;
    			else if(t2 < 0) t2 = i;
    		}
    		if(cnt == 1){ f[s] = 1; continue; }
    		for(int ts = (s - 1 & s); ts; ts = (ts - 1 & s)) if((ts >> t2 & 1) && !(ts >> t1 & 1)) {
    			f[s] += (LL)f[ts] * f[s^ts] % MOD * (mul[t1][ts] + MOD - 1) % MOD;
    			if(f[s] >= MOD) f[s] -= MOD;
    		}
    	}
    	
    	printf("%d
    ", f[all]);
    	
    	return 0;
    }
    
  • 相关阅读:
    osu合集(期望dp)
    P1640 [SCOI2010]连续攻击游戏
    P2155 [SDOI2008]沙拉公主的困惑
    P3419 [POI2005]SAMToy Cars / SP688 SAM Toy Cars
    P3586 [POI2015]LOG
    P4147 玉蟾宫
    P4454 [CQOI2018]破解DH协议
    射击小游戏一02(玩家和怪物添加)
    ASCII,Unicode和UTF8
    Android中MenuInflater实例
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7998910.html
Copyright © 2011-2022 走看看