HDU

原题链接

题意

有N个灯和M个开关,每个开关控制着一些灯,如果按下某个开关,就会让对应的灯切换状态;问在每个开关按下与否的一共2^m情况下,每种状态下亮灯的个数的立方的和。

思路
1、首先注意到N<=50,M<=50,因此很容易想到状压;

2、考虑X^3,其中X就是每种状况下亮着的灯的数量;

3、如何解这个X^3?我们把它展开——
X=x1+x2+x3+...+xn,其中xi是第i个灯的亮或暗状况;
因此X^3=(x1+x2+x3+...+xn)*(x1+x2+x3+...+xn)*(x1+x2+x3+...+xn)=Σxi*xj*xk (1<=i<=j<=k<=n);

4、dp[m][state]代表前m个开关,达成状态为state的方案数,其中state从(000)2~(111)2,代表三个灯的亮或灭;在其基础上dp就行了;遍历i,j,k,同时将开关的控制状态压缩。

5、答案每次加上dp[m][7],因为只有xi=xj=xk=1时,才对X^3有贡献,总复杂度O(n^3*m);

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define ull unsigned long long
#define LOCAL

using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;

ll d[51][8];
ll s[51];

int main(){
    int t,n,m,num;
    cin>>t;
    for(int kase=1;kase<=t;kase++){
        memset(s,0,sizeof(s));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d",&num);
            for(int j=1;j<=num;j++){
                int t;
                scanf("%d",&t);
                s[i]|=(1LL<<t);///状压
            }
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int k=1;k<=n;k++){
                    memset(d,0,sizeof(d));
                    d[0][0]=1;
                    for(int x=1;x<=m;x++){
                        for(int y=0;y<8;y++){
                            int t=y;
                            if(s[x]&(1LL<<i)) t^=1;
                            if(s[x]&(1LL<<j)) t^=2;
                            if(s[x]&(1LL<<k)) t^=4;
                            d[x][y]+=d[x-1][y];
                            d[x][t]+=d[x-1][y];
                        }
                    }
                    ans=(ans+d[m][7])%mod;
                }
            }
        }
        printf("Case #%d: %lld
",kase,ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/fht-litost/p/7265723.html