【poj3875】 Lights

http://poj.org/problem?id=3875 (题目链接)

题意

  有M个N位的不同的二进制数,他们异或起来前v位等于1,求这m个数的不同组合方式(同一组数不同顺序不算)。

Solution

  如果任意两个数可以相同就非常好做了,然而。。事实总是那么令人悲伤。

  考虑dp。假设最后异或出来的数为${V}$,令${f_m}$表示m个数的不同方案(这里我们先求解不同顺序算不同方案的,更好处理一些)。那么如果我们先选出其中${m-1}$个数,它的方案就是:

$${(m-1)!×C_{2^n}^{m-1}}$$

  这很好理解,一共有${2^n}$个数可供选择,从中任意选取${m-1}$个数,令其为${a_1,a_2,a_3······a_{m-1}}$那么剩下的第${a_m}$个数就可以确定了:

$${\because a_1~XOR~a_2~XOR······XOR~a_m=V}$$

$${\therefore a_m=V~XOR~a_1~XOR~a_2~XOR······XOR~a_{m-1}}$$

  然而问题来了,如果${a_m}$求出来与之前已经选择好了的${m-1}$个数中的某一个相等怎么办?

  假设${a_m=a_1}$,因为相同的数异或起来等于0,则有:

$${a_2~XOR~a_3~XOR······XOR~a_{m-1}=V}$$

  这个式子的方案数代表什么,不就是代表${f_{m-2}}$吗。所以我们可以列出dp方程:

$${f_m=(m-1)!×C_{2^n}^{m-1}-(m-1)×(2^n-(m-2))×f_{m-2}}$$

  其中第一项${(m-1)!×C_{2^n}^{m-1}}$很好理解,就是不考虑${a_m}$与之前已经选好的数相同的方案数。

  第二项中${(m-1)}$表示${a_m}$可以与${m-1}$个数中的任意一个相同,${(2^n-(m-2))}$表示${a_m}$的值的选择方案。

  这里还要注意一个地方:${C_{2^n}^{m-1}}$怎么求。考虑Lucas定理求解组合数取模:

$${Lucas(n,m)=C_{n~mod~p}^{m~mod~p}*Lucas(n/p,m/p)}$$

  因为m最大为1000,恒小于模数p,所以${Lucas(n/p,m/p)=1}$,所以:

$${C_{2^n}^{m-1}=C_{2^{n}~mod~p}^{m~mod~p}}$$

$${ans=\frac{f[m]}{m!}}$$

细节

  此时,问题已经得到了解决。什么?你说过不了样例?

  嘿嘿嘿,→_→。

  显然${f[0]=0,f[1]=1}$,那么${f[2]=?}$。

  考虑${v=0}$的情况,因为两个数不能相同,而只有相同的两个数的异或和等于0,所以${f[2]=0}$。

  而当${v>0}$时,${f[2]=C_{2^n}^{1}}$。

  此时,问题已经得到了解决(真的)

代码

// poj3875
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define MOD 10567201
#define Pi acos(-1,0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=2000;
LL f[maxn],fac[maxn],C[maxn];
int n,m,v;

LL power(LL a,LL b) {
	LL res=1;
	while (b) {
		if (b&1) res=res*a%MOD;
		b>>=1;a=a*a%MOD;
	}
	return res;
}
int main() {
	fac[0]=1;for (int i=1;i<=1000;i++) fac[i]=fac[i-1]*i%MOD;
	while (scanf("%d%d%d",&n,&m,&v)!=EOF) {
		if (!n && !m && !v) break;
		LL tmp=power(2,n);
		C[0]=1;
		for (int i=1;i<=m;i++) {
			int inv=power(i,MOD-2);
			C[i]=C[i-1]*(tmp-i+1)%MOD*inv%MOD;
		}
		f[1]=1;f[0]=0;if (v==0) f[2]=0;else f[2]=tmp;
		for (int i=3;i<=m;i++) {
			f[i]=(fac[i-1]*C[i-1]%MOD-(i-1)*(tmp-(i-2))%MOD*f[i-2]%MOD+MOD)%MOD;
		}
		LL inv=power(fac[m],MOD-2);
		printf("%lld\n",inv*f[m]%MOD);
	}
    return 0;
}

  

posted @ 2016-12-29 22:28  MashiroSky  阅读(480)  评论(1编辑  收藏  举报