2019湖南省赛J Parity of Tuples (Easy)(状压DP+贡献)

题目链接

题目大意:

给你n个含m个元素的向量 a 1 , a 2 , . . , a n a_1,a_2,..,a_n a1,a2,..,an,对于一个给定的k,求 ∑ x = 0 2 k − 1 c o u n t ( x ) ⋅ 3 x \sum_{x = 0}^{2^k - 1} \mathrm{count}(x) \cdot 3^x x=02k1count(x)3x modulo ( 1 0 9 + 7 ) (10^9+7) (109+7),其中count(x)是满足对(1<=j<=m)x& a i , j a_{i,j} ai,j中1的数目为奇数的向量个数。

解题思路:

我们发现可以对于每个向量单独算贡献,那么问题就转换成对于一个包含最多10个元素的向量,求 ∑ x = 0 2 k − 1 3 x \sum_{x = 0}^{2^k - 1} 3^x x=02k13x modulo ( 1 0 9 + 7 ) (10^9+7) (109+7),其中x需要满足和每个元素的与的1的个数为奇数。那么我们把x二进制拆分后可以算 3 2 0 , 3 2 1 , 3 2 2 , . . 3 2 k − 1 3^{2^0},3^{2^1},3^{2^2},..3^{2^{k-1}} 320,321,322,..32k1的贡献。我们从低位到高位考虑,用mask来代表每种元素奇偶性,第i位为1代表第i个元素的1的个数为奇数,设 d p [ i ] [ m a s k ] dp[i][mask] dp[i][mask]表示0到i-1位中,每个元素的1的个数奇偶性为mask的时候的贡献,那么x的第i位取0和取1可以分别转移到dp[i+1][mask]和dp[i+1][mask ^ amask],其中amask代表第i位为1的元素的集合,第i位取1会改变这些元素的1的个数的奇偶性。
最后dp[k][(1<

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const ll mod = 1e9 + 7;
ll dp[31][1<<11];
int a[12], m , k;
ll bin[33];
ll sol(){
    for(int i = 0; i <= k; ++i) for(int j = 0; j < (1<<m); ++j) dp[i][j] = 0;
    for(int i = 0; i < m; ++i) scanf("%d", &a[i]);
    dp[0][0] = 1;
    for(int i = 0; i < k; ++i){
        int amask = 0;
        for(int j = 0; j < m; ++j) if((a[j]>>i)&1) amask |= (1<<j);
        for(int mask = 0; mask < (1<<m); ++mask){
            dp[i+1][mask] = (dp[i+1][mask] + dp[i][mask])%mod;
            dp[i+1][mask^amask] = (dp[i+1][mask^amask] + bin[i]*dp[i][mask])%mod;
        }
    }
    return dp[k][(1<<m)-1];
}
int main()
{
    bin[0] = 3; for(int i = 1; i < 33; ++i) bin[i] = bin[i-1]*bin[i-1]%mod;
    int n;
    while(scanf("%d%d%d", &n, &m, &k)!=EOF){
        ll ans = 0;
        while (n--) ans =(ans + sol())%mod;
        printf("%lld\n", ans);
    }
}

你可能感兴趣的:(dp)