题面
<center> 5758: [Jsoi2016]位运算<center>
<center>时间限制:20秒 内存限制:512MB <center>
题目描述
JYY最近在研究位运算。他发现位运算中最有趣的就是异或(xor)运算。对于两个数的异或运算,JYY发现了一个结论:两个数的异或值为0当且仅当他们相等。于是JYY又开始思考,对于N个数的异或值会有什么性质呢?
【问题描述】
JYY想知道,如果在0到R-1的范围内,选出N个不同的整数,并使得这N个整数的异或值为0,那么一共有多少种选择的方法呢?(选择的不同次序并不作重复统计,请参见样例)JYY是一个计算机科学家,所以他脑海里的R非常非常大。为了能够方便的表达,如果我们将R写成一个0-1串,那么R是由一个较短的0-1串S重复K次得到的。比如,若S=“101”,K=2,那么R的2进制表示则为“101101”。由于计算的结果会非常大,JYY只需要你告诉他选择的总数对10^9+7取模的结果即可。
输入
第一行包含两个正整数N和K。
接下来一行包含一个由0和1组成的字符串S
我们保证S的第一个字符一定为1。
3<=N<=7,1<=k<=10^5,1<=|S|<=50
输出
一行一个整数,表示选择的方案数对10^9+7取模的值。
样例输入
3 1
100
样例输出
1
提示
对于第一个样例, 唯一的一种选择方法是选择 {1, 2, 3}。
题解
我推出了规律:所有N个数的第i位中‘1’的个数必须为偶数,才能使最终结果为0,但是接下来就没有思路了……
在网上搜了题解:题解链接,但是题解过于简单,代码也没啥注释,也许是wtcl并没有看明白,又研究了好久才搞懂,并把代码加上了注释。
大体思路是这样的:首先,定义了一个结构体用来定义矩阵,其中包括矩阵的初始化(类似于Java的构造方法);然后定义了两个函数(方法):矩阵的乘法和矩阵幂;以上都是辅助性质的。以下为主要内容:为实现N个数不相同,规定A~1~<A~2~<……<A~N~,用trans函数(方法)实现,然后进行状态压缩用lim的二进制值来表示N个数每一位的取值(0 or 1),最后求出一个|s|的结果,再运用矩阵快速幂求出k个|s|的结果。
源码
#include <iostream>
#include <cstring>
using namespace std;
const int mod=1e9+7;
int n,k,len,lim,f[55][130];
char s[55];
typedef unsigned long long ull;
struct matrix {//矩阵结构体定义
ull a[130][130];
matrix(int t=0) {//初始化一个对角线为t,其他位置为0的矩阵
memset(a,0,sizeof(a));
for(int i=0;i<=lim;i++) a[i][i]=t;
}
}A;
matrix fun(matrix a,matrix b) {//矩阵乘法
matrix c(0);
for(int i=0;i<=lim;i++)
for(int k=0;k<=i;k++) if(a.a[i][k])
for(int j=0;j<=k;j++)
((c.a[i][j]+=(ull)a.a[i][k]*b.a[k][j]) >= mod) ? c.a[i][j]%=mod : 0;
for(int i=0;i<=lim;i++)
for(int j=0;j<=i;j++)
(c.a[i][j]>=mod) && (c.a[i][j]%=mod);
return c;
}
matrix power(matrix a,int b){//矩阵快速幂
matrix rs(1);
for(;b;b>>=1, a=fun(a,a)) if(b&1) rs=fun(rs,a);
return rs;
}
int trans(int pre,int now,int pos) {//判断每一位的N个数是否按递增排序
int c=0;
for(int j=n-1;j;j--)
if((pre>>j)&1) {
int a=(now>>j)&1, b=(now>>(j-1))&1;
if(a>b) return -1;//此时~-1=0
if(a==b) c|=(1<<j);
}
if(pre&1) {
int tl=0;
while(tl<n && (pre>>tl)&1)
if((now>>tl)&1 && s[pos]=='0') return -1;//此时~-1=0
else ++tl;
if((now&1)==s[pos]-'0') c|=1;
}
return c;
}
int main(int argc, char** argv) {
cin>>n>>k>>(s+1);
len=strlen(s+1);
lim=(1<<n)-1;//状态压缩:用来表示n个数每一位的取值
for(int i=0;i<=lim;i++) {
memset(f,0,sizeof(f));
f[0][i]=1;
for(int j=1;j<=len;++j)
for(int pre=0;pre<=lim;++pre) //之前的状态
for(int now=0;now<=lim;++now) { //现在要放的状态
if(__builtin_popcount(now)&1) continue;
// __builtin_popcount获取now的二进制数中1的个数,只有1的个数为偶数时才可能取异或为0
int t=trans(pre,now,j);//判断N个数的第j位是否递增
if(~t) f[j][t]=(f[j][t]+f[j-1][pre])%mod;//t=-1时,~t=0,不进行
}
for(int j=0;j<=lim;j++)//获取最终结果
A.a[i][j]=f[len][j];
}
matrix B=power(A,k);//进行矩阵快速幂
int ans=B.a[lim][0]%mod;//得出最后结果
cout<<ans;
return 0;
}