HDU 5117 Fluorescent(状压DP)

Description
有n个灯泡,初始均为关闭状态,m个开关,每个开关可以改变一些灯泡的状态(开变关,关变开),打开每个开关的操作是随机等概率的,以X表示每次开着灯泡的数量,求E(x^3) mod 1e9+7
Input
第一行为一整数T表示用例组数,每组用例第一行为两个整数n和m分别表示灯泡数量和开关数量,之后m行每行表示一个开关控制的灯泡数量和编号
Output
对于每组用例,输出E(X^3) mod 1e9+7
Sample Input
2
2 2
1 1
2 1 2
3 1
3 1 2 3
Sample Output
Case #1: 10
Case #2: 27
Solution
以x1,x2,…,xn表示n个灯泡的状态(开1关0)
X^3=(x1+x2+…+xn)(x1+x2+…+xn)(x1+x2+…+xn)=sum(xi*xj*xk),1<=i,j,k<=n
固定i,j,k,以dp[s][t]表示操作前s个开关后这三个灯泡的开闭状态为t的情况数(0<=t<=7),那么有dp[s][t]=dp[s-1][t]+dp[s-1][t^temp],其中temp为第s个开关对这三个灯泡操作的状态(例如temp=7表示打开这三个开关,temp=3表示打开前两个灯泡),此处dp数组第一位还可以滚动来节省空间
枚举i,j,k,累加dp[m][7]即为答案
Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 55
#define mod 1000000007ll
typedef long long ll;
int T,n,m,flag[maxn][maxn];
ll dp[2][8];
int main()
{
    scanf("%d",&T);
    for(int Case=1;Case<=T;Case++)
    {
        int num,temp;
        memset(flag,0,sizeof(flag));
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d",&num);
            while(num--)
            {
                scanf("%d",&temp);
                flag[i][temp-1]=1;
            }
        }
        ll ans=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                {
                    memset(dp,0,sizeof(dp));
                    dp[0][0]=1;
                    for(int s=0;s<m;s++)
                    {
                        temp=flag[s][i]+2*flag[s][j]+4*flag[s][k];
                        for(int t=0;t<8;t++)
                            dp[(s+1)%2][t]=(dp[s%2][t]+dp[s%2][t^temp])%mod;
                    }
                    ans=(ans+dp[m%2][7])%mod;
                }
        printf("Case #%d: %lld\n",Case,ans);
    }
    return 0;
}

你可能感兴趣的:(HDU 5117 Fluorescent(状压DP))