hdu5117

Xi表示第i个灯最后亮不亮,亮为1,不亮为0。

所以X=∑Xi,X^3=(X1+X2……)(X1+X2……)(X1+X2……);

根据期望的线性性,考虑每一项XiXjXk的贡献。

当且仅当i,j,k都亮时,贡献为1。

对灯i,用一个bitset维护能改变它状态的开关集合。第p位为1表示第p个开关能改变灯i的状态。

对i,j,k三个灯,求出能同时改变三个灯状态的个数c,能改变两个灯状态的个数b1,b2,b3,能改变一个灯状态的个数a1,a2,a3,和都不能改变的个数cnt=m-c-b1-b2-b3-a1-a2-a3;

对于都不能改变的,这些开关状态可以任意,所以最后乘上(1<<cnt)。

对于能改变的,我用的纯暴力。。。。。

枚举C,B1,B2,B3分别表示在c,b1,b2,b3中开几个开关。

根据d1=C+B1+B2的奇偶性,可以知道在a1中要开奇数个还是偶数个。根据二项式定理,取奇数个和偶数个方案数相同,都是(1<<(a1-1));(注意d1为0时,里面至少要取1)

d2,d3同理。

然后因为暴力枚举i,j,k全不同会出现6次,两个相同会出现3次,都相同会出现1次,所以枚举的时候让i<=j<=k可以常数优化。

在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<bitset>
using namespace std;
#define ll long long 
const ll mod=1e9+7;
int n,m;
ll two[56],CC[56][56];
bitset<56>a[56],tmp;
int main()
{
	int icase=0;
	two[0]=1;
	for(int i=1;i<=53;i++)two[i]=two[i-1]*2%mod;
	for(int i=0;i<=53;i++)
	{
		CC[i][0]=1;
		for(int j=1;j<=i;j++)
		{
			CC[i][j]=CC[i-1][j]+CC[i-1][j-1];
			if(CC[i][j]>=mod)CC[i][j]-=mod;
		}
	}
	int _;scanf("%d",&_);
	while(_--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)a[i].reset();
		int cnt;
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&cnt);
			for(int j=1;j<=cnt;j++)
			{
				int x;
				scanf("%d",&x);
				a[x].set(i);
			}
		}
		ll ans=0;
		for(int i=1;i<=n;i++)
			for(int j=i;j<=n;j++)
				for(int k=j;k<=n;k++)
				{
					tmp=a[i]&a[j]&a[k];
					int c=tmp.count();
					int b1=((a[i]&a[j])^tmp).count();
					int b2=((a[j]&a[k])^tmp).count();
					int b3=((a[k]&a[i])^tmp).count();
					int a1=a[i].count()-c-b1-b3;
					int a2=a[j].count()-c-b1-b2;
					int a3=a[k].count()-c-b2-b3;
					ll sum=0;
					for(int C=0;C<=c;C++)
						for(int B1=0;B1<=b1;B1++)
							for(int B2=0;B2<=b2;B2++)
								for(int B3=0;B3<=b3;B3++)
								{
									int d1=C+B1+B3,d2=C+B1+B2,d3=C+B2+B3;
									ll mul=CC[c][C]*CC[b1][B1]%mod*CC[b2][B2]%mod*CC[b3][B3]%mod;
									ll tmp1,tmp2,tmp3;
									if(a1>=1)tmp1=two[a1-1];
									else if(d1&1)tmp1=1;
									else tmp1=0;
									if(a2>=1)tmp2=two[a2-1];
									else if(d2&1)tmp2=1;
									else tmp2=0;
									if(a3>=1)tmp3=two[a3-1];
									else if(d3&1)tmp3=1;
									else tmp3=0;
				
									sum+=tmp1*tmp2%mod*tmp3%mod*mul%mod;
									if(sum>=mod)sum-=mod;
								}
					ll cnt=6;
					if(i==j&&j==k)cnt=1;
					else if(i==j||j==k||i==k)cnt=3;
					ans+=sum*two[m-c-a1-a2-a3-b1-b2-b3]%mod*cnt%mod;
					if(ans>=mod)ans-=mod;
				}
		printf("Case #%d: %I64d\n",++icase,ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值