• BZOJ 2560(子集DP+容斥原理)


    2560: 串珠子

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 757  Solved: 497
    [Submit][Status][Discuss]

    Description

      铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
      现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
      铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。
     

    Input

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

     

    Output

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

    Sample Input

    3
    0 2 3
    2 0 4
    3 4 0

    Sample Output

    50

    HINT

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

    题解

    这题是一个状压DP,或者说子集DP。。

    设计两个数组,f[i]代表构成一个状态为i的连通图的方案数。

    g[i]代表构成一个状态为i的图(不保证联通)的方案数。

    然后g[i]可以枚举i中的每一个有序点对对应的a[i][j]+1的乘积求出。

    比如i在二进制下为1011,所以g[i]就是a[1][2]*a[1][4]*a[2][4];

    那么f[i]怎么求呢?可以用容斥。

    当前点集为联通图的方案数等于总方案数-一个子集是连通图的方案数*这个子集的补集不保证是连通图的方案数。

    那么我们枚举子集就可以了。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const long long mod=1e9+7;
     8 const long long N=20;
     9 long long n,a[N][N],g[1<<20],f[1<<20];
    10 long long lowbit(long long x){
    11     return x&-x;
    12 }
    13 int main(){
    14     scanf("%lld",&n);
    15     for(long long i=1;i<=n;i++)
    16         for(long long j=1;j<=n;j++){
    17             scanf("%lld",&a[i][j]);
    18         }
    19     for(long long i=1;i<=(1<<n)-1;i++){
    20         g[i]=1;
    21         for(long long j=1;j<=n;j++){
    22             if((1<<j-1)&i){
    23                 for(long long k=j+1;k<=n;k++){
    24                     if((1<<k-1)&i){
    25                         g[i]=(g[i]*(a[j][k]+1))%mod;
    26                     }
    27                 }
    28             }
    29         }
    30         f[i]=g[i];
    31         long long now=i^lowbit(i);
    32         for(long long j=now;j;j=(j-1)&now){
    33             f[i]=((f[i]-f[i^j]*g[j])%mod+mod)%mod;
    34         }
    35     }
    36     printf("%lld",f[(1<<n)-1]);
    37     return 0;
    38 } 
  • 相关阅读:
    生成器
    迭代器
    装饰器
    闭包函数
    函数对象
    函数
    文件
    字符编码
    基本的数据类型和内置方法02
    基本的数据类型和内置方法01
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/9719407.html
Copyright © 2020-2023  润新知