D. 无聊的数学题
Time limit per test: 1.0 seconds
Memory limit: 512 megabytes
这是一个无聊的数学题。
非形式化地说:从 0,1,…,2n−1 中选出若干个数,使得这些数的异或和为 k。求有多少种方案?
形式化地说:现给出 n,p,k,输出 {0,1,…,2n−1} 有多少个非空子集异或和恰好为 k。输出结果模 p 的值。
Input
一行三个整数 n, k, p (1≤n≤1018, 0≤k≤min(2n−1,1018), 2≤p≤109, p 是质数)。
Examples
input
2 3 998244353
output
4
Note
样例解释:k=3,有以下四个方案:{1,2}, {3}, {0,3}, {0,1,2}。
在数字逻辑中,异或是对两个运算元的一种逻辑分析类型,符号为 XOR 或 ⊕。与一般的逻辑或 OR 不同,当两两数值相同为否,而数值不同时为真。对于任意两个数的异或:先写出这两个数的二进制表示,然后对于每一位进行异或。在 C 语言中写作 a ^ b
。
题解:
当 p 不等于 2 时,我们可以想到的是
这里给出指数节公式
根据公式
综上
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll n,k,p; ll mut(ll a,ll b,ll mod) { ll ans = 0; while(b) { if(b & 1) ans = (ans + a) % mod; a = (a + a) % mod; b >>= 1; } return ans; } ll power(ll a,ll b,ll mod) { a %= mod; ll ans = 1; while(b) { if(b & 1) ans = mut(ans,a,mod); a = mut(a,a,mod); b >>= 1; } return ans; } ll phi(ll n) { ll ans = n; for(ll i = 2;i*i<=n;i++) { if(n % i == 0) { ans = ans - ans / i; while(n % i == 0) n /= i; } } if(n > 1) ans = ans - ans / n; return ans; } ll inv(ll a,ll mod) { return power(a,mod-2,mod)%mod; } int main() { while(~scanf("%lld%lld%lld",&n,&k,&p)) { ll ans = 0; if(p != 2) { ll phip = phi(p); ll a = power(2,n,phip) + phip; a = power(2,a,p); ll b = power(2,n,p); ans = a * inv(b,p) % p; if(!k) ans--; ans = (ans % p + p) % p; } else { ans = 0; if(!k) ans = 1; } printf("%lld\n",ans); } return 0; }