Moamen and XOR-CF

Moamen and XOR

题目链接:Problem - C - Codeforces

题目大意:

​ M和E玩游戏,有n个不超过2**k的的非负数,M对n个数取&得到p1,E对n个数取^得到p2.若p1>=p2,则M胜利。给定n和k,输出M赢的次数。

**思路:

思考这个问题的时候要先明确n、k代表了什么。可以形象地将其理解为一个“二维矩阵”,k是列数,n是行数。每一列代表每一个二进制位,每一行代表每一个数字。

M赢的条件是p1>=p2,拆分开就是p1>p2和p1==p2。对两种情况深入讨论:

1)p1 == p2:

1a:p1 p2的每一位都是0(二进制位):

​ 对第i位(二进制位),&操作需要在n个数中至少有一个0,^需要有偶数个1就是说要在n位里取偶数位x,就是C(x,n),(x取偶数)种可能性,为了方便把i位为0的可能性记作A。而一个数一共有k位,总可能性为 A**k。
1b:p1 p2每一位都是1:

​ 对第i位,&需要全1,^需要奇数个1 。总结一下,n是奇数。就是说只有n是奇数才有可能出现第i位经过&或^后都是1。

2)p1 > p2:

​ 就是说在1~k位种总能找到一个i,p1[i] == 1 && p2[i] == 0 && (1i-1位完全相等)。只要第i位p1是1,p2是0,不管i+1n怎么变,p1就是大于p2的。当&运算出1时就是说n个数的第i位全是1,当^运算出0时就是说n个数里有偶数个1。两个条件叠加,得出n是偶数的条件。

​ 前i-1位相同的可能性是A**(i-1),i+1~k位可以随便01,那就是2**(k-i),加之有n个数字,可能性变为2**(k-i)*n。综合一下就是A**(i-1) * 2**(k-i)*n.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<bitset>
#include<sstream>
#include<cstdlib>
//#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const ll N = 2*1e5+50;
ll fac[N];
ll n,k,ans,bit_0_psib,T;

inline ll read(){
    ll ans = 0;
    char c = getchar();
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c)){
        ans = ans * 10 + c - '0';
        c = getchar();
    }return ans;
}

inline void init(ll n){
	fac[0] = 1;//初始化阶乘
	for(ll i = 1;i <= n; i++)
		fac[i] = fac[i-1] * i % mod;
	
}

inline ll quick_pow(ll a, ll k, ll p) {
	ll res = 1;//快速幂
	a %= p;
	while (k) {
		if (k & 1) res = res * a % p;
		a = a * a % p;
		k >>= 1;
	}
	return res;
}

inline ll inv(ll x,ll p){//逆元
	return quick_pow(x,p-2,p);
}

inline ll getC(ll n,ll m,ll p){//求组合数
	return fac[n] * inv(fac[n-m] * fac[m] % p, p) % p;
}

inline ll getA(ll n,ll p){
	ll res = 0;
	for(ll i = 0;i < n; i+=2){
		res = (res + getC(n,i,mod))%mod;
	}
	return res;
}

int main(){
	init(2e5);
	T = read();
	while(T--){
		n = read();
		k = read();	
		ans = 0;
		bit_0_psib = getA(n,mod);//一位是0的可能性
		if(n&1){
			ans = quick_pow(bit_0_psib+1,k,mod);
		}else{
			for(ll i = 1;i <= k; i++){
				ans = (ans + quick_pow(bit_0_psib,i-1,mod) * quick_pow(quick_pow(2,k-i,mod),n,mod)%mod)%mod;
			}
			ans =(ans + quick_pow(bit_0_psib,k,mod))%mod;
		}
		printf("%lld\n",ans);
	}
}

写在后面:

​ 这类题目一定不能傻傻去模拟,而是要分析小部分的情况。将一个数放到二进制层面去理解。

posted @ 2021-08-15 21:39  tyrii  阅读(89)  评论(0)    收藏  举报