题意:有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;
}