HDU 5117 Fluorescent (数学 状压DP)

题意:有n个灯泡,初始均为关闭状态,m个开关,每个开关可以改变一些灯泡的状态(开变关,关变开),打开每个

开关的操作是随机等概率的,以X表示每次开着灯泡的数量,求E(x^3) *2^m % 1e9+7


思路:原式可以分解为∑X3 ,X =(x1+x2+x3...+xn),其中xi表示第i个灯的状态,题目就转化为求各个状态下X的立方

X^3=(x1+x2+x3...xn)*(x1+x2+x3...xn)*(x1+x2+x3...xn) = ∑(Xi * Xj * Xk) (1 <= i, j,k <= n),那么我们只要

于所有xi*xj*xk在所有状态中能为1的方法数,也就是i,j,k三灯全亮的方法数,加起来就是答案。


固定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]即为答案

(摘自:点击打开链接


完整思路:点击打开链接

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

2、最开始考虑的是算出每种情况下对应的方案数,然后依次dp,但是数据量太大;

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

4、如何解这个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);

5、dp[m][state]代表前m个开关,达成状态为state的方案数,其中state从(000)2~(111)2,代表三个灯的亮或灭;在其基础上dp就行了;

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


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 55;
const int mod = 1e9+7;
ll d[maxn], dp[maxn][maxn];
int n, m;

int main(void)
{
    int t, ca = 1;
    cin >> t;
    while(t--)
    {
        memset(d, 0, sizeof(d));
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; i++)
        {
            int k;
            scanf("%d", &k);
            while(k--)
            {
                int tmp;
                scanf("%d", &tmp);
                d[i] |= 1LL<<(tmp-1);
            }
        }
        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(dp, 0, sizeof(dp));
                    dp[0][0] = 1;
                    for(int l = 1; l <= m; l++)
                    {
                        int tmp = 0;
                        if((d[l]>>(i-1))&1) tmp += 1;
                        if((d[l]>>(j-1))&1) tmp += 2;
                        if((d[l]>>(k-1))&1) tmp += 4;
                        for(int m = 0; m < 8; m++)
                        {
                            dp[l][m] += dp[l-1][m]; //不按
                            dp[l][m] += dp[l-1][m^tmp]; //按
                        }
                    }
                    ans = (ans+dp[m][7])%mod;
                }
        printf("Case #%d: %lld\n", ca++, ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值