状压dp Fluorescent开关灯

题目地址

题目大意:

n个灯,m个开关,每个开关能控制一些灯的状态,即关闭的打开,打开的关闭。一开始灯全部是关闭的。现在有个人去操作这些开关。现在问这个人操作完这些开关后,亮着的灯的期望E(x),但是题目要求输出的是E(x^3)*2^m%(1e9+7)。

解题思路:m个开关,显然会2^m次组合首先考虑求E(x)。显然就是将这2^m种开关的开闭组合求出来每种情况的灯亮着的个数和sum(x),然后sum(x)/(2^m)就是E(x)。考虑在这2^m次中,每栈灯的贡献。

dp[i][j]表示前j个开关能将第i栈灯开启和关闭的方案数。

现在考虑求E(x^3)*2^m。显然就是就那个sum(x^3)。

下面就是还有点不懂的地方了:

设x=a1+a2+……+an ,ai  代表第i栈灯亮与不亮的情况。

那么x^3=(a1+a2+……+an)*(a1+a2+……+an)*(a1+a2+……+an)。

展开就是Σ(ai*aj*a)。

所以我们考虑枚举i,j,k,然后将i,j,k的状态压缩起来。

然后求解贡献的思路就和上面一样了。

对于x=a1+a2+……+an ,ai  代表第i栈灯亮与不亮的情况。始终不理解 。分解成三个for循环也不太理解为什么最后一定是这三个灯都亮着的情况dp[7][m];

这个题可以算是看到现在最有意思的题了,再就是数据可能是long long型,1ll<<x看了好久。。一直以为是三个一,很是懵。。。后来问同学才知道是一个一,两个L,代表long long型。。不过这几个处理还不是特别明白~sum(x)可以想象,sum(x^3),得继续在看看、

代码:

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=(a);i<=(b);++i)
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;

const int N=57,P=1e9+7;
int t,n,m,ans,cas,dp[8][N],num,x;
ll sw[N];

void up(int &a,int b){a+=b;if(a>=P)a-=P;}

int main(){
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m),mst(sw,0),ans=0;
        F(i,1,m)
        {
            scanf("%d",&num);
            F(j,1,num)scanf("%d",&x),sw[i]|=1ll<<x;
        }
        F(i,1,n)F(j,1,n)F(k,1,n)
        {
            mst(dp,0);
            dp[0][0]=1;
            F(l,0,m-1)F(u,0,7)if(dp[u][l])
            {
                int nxt=u;
                if(sw[l+1]&(1ll<<i))nxt^=1;
                if(sw[l+1]&(1ll<<j))nxt^=2;
                if(sw[l+1]&(1ll<<k))nxt^=4;
                up(dp[nxt][l+1],dp[u][l]);//开
                up(dp[u][l+1],dp[u][l]);//关
            }
            up(ans,dp[7][m]);
        }
        printf("Case #%d: %d\n",++cas,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值