HDU - 5117 Fluorescent(状压dp+思维)
题意
有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\n",kase,ans); } return 0; }