NC 210732 灯谜
题目描述
吉吉国王在探险的时候发现了一个奇怪的游戏,这个游戏有n盏灯,每盏灯刚开始都是熄灭的。有灯那么必然就有开关,吉吉国王在另外一侧发现了个开关,对于每个开关,都控制着一定数量的灯。对于每个开关,吉吉国王可以选择按一下,或者不按,每次按下,这个开关都会让其控制的灯的状态取反。 设x是最后亮着的灯的个数,现在需要求E(x3) * 2^m的值,EEE表示取期望。只需要输出在模1e9+7意义下的答案。
输入描述:
第一行两个整数n,m。
接下来m行,每行开头一个整数k表示第i个开关控制的灯的个数,接下来k个整数表示控制的灯的编号。
输出描述:
输出一个整数表示答案。
示例1
输入
4 2
3 1 2 4
2 3 4
输出
62
备注:
1≤n,m≤50 1≤k≤n
思路:这题是管道取珠的变形题:https://www.cnblogs.com/studyshare777/p/13601390.html
首先看E(x3) * 2^m的物理意义,emm,其实就是按开关结果的期望 * 按开关的总方案数,就是x^3。
求x^3,和管道取珠一样的思路,即加2个完全相同的装置,设灯i属于装置1,灯j属于装置2,灯k属于装置3,因为x^3就是3个装置亮的灯数量相同的方案数。所以当灯i,j,k都亮的时候,就是对答案有贡献。
然后对每组(i,j,k),求他们同时亮的方案数:设f[t] [st],为对前t个开关操作过后,3个灯状态为st的方案数,st二进制位表示i,j,k的灯亮灭状态。
显然有2种情况,不按第t个开关,f[t] [st]+=f[t-1] [st],按下第t个f[t] [st]+=f[t-1] [st'],状态st'按下开关后得到st。(具体注释在代码)
#include<iostream> #include<algorithm> #include<fstream> #include<cstring> #include<cstdio> #include<sstream> #include<vector> #include<stack> #include<deque> #include<cmath> #include<map> #include<queue> #include<bitset> //#include<hash_map> #define sd(x) scanf("%d",&x) #define lsd(x) scanf("%lld",&x) #define ms(x,y) memset(x,y,sizeof x) #define fu(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define all(a) a.begin(),a.end() #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; //using namespace __gnu_cxx; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int maxn=1e5+79; const int mod=1e9+7; const int INF=1e9+7; const double pi=acos(-1); bool charge[56][56]; ll f[56][10];//f[t][st]:按前t个开关,3个灯状态为st int n,m; ll cal(int i,int j,int k) { //计算i,j,k灯全亮的方案数 ms(f,0); f[0][0]=1; fu(turn,1,m) { //当前开关 fu(p,0,7) { //当前状态p,二进制位上,1表示亮,0表示不亮 f[turn][p]+=f[turn-1][p];//不按第t个开关 //上一个状态lp int lp=p; //按下开关,对应灯取反 if(charge[turn][i]) lp=(lp^(1<<2)); if(charge[turn][j]) lp=(lp^(1<<1)); if(charge[turn][k]) lp=(lp^1); f[turn][p]+=f[turn-1][lp];//按下第t个开关 } } return f[m][7];//3盏灯都亮方案数 } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); //std::ifstream fil; //fil.open("t.txt"); //freopen("C.in","r",stdin); cin>>n>>m; fu(i,1,m) { int k;cin>>k; fu(j,1,k) { int x;cin>>x; //开关i控制灯x charge[i][x]=1; } } ll ans=0; fu(i,1,n) { fu(j,1,n) { fu(k,1,n) { //设为3个不同装置,枚举每个装置灯i,j,k ans=(ans+cal(i,j,k))%mod; } } } cout<<ans<<endl; return 0; }