HDU 6189 - Law of Commutation ( 规律 + 思路 )
题意
给出n, a ( n ≤ 30, 1 ≤ a ≤ 109. ), m = 2^n, 求[1, m]范围内有多少个数b使得 a^b mod m == b^a mod m
思路
打表找规律
1. 若a为奇数 cnt == 1
2. 若a为偶数 :
当 b <= n 时, 因为数据量较小可以直接暴力求
当 b > n 时, a^b % m 必然 = 0 要求有b使得 b^a % m = 0, 所以b必然为偶数
具体实现见代码
传说中的官方题解 :
I Law of Commutation
对于a为奇数的情况,b一定为奇数,下证b=a mod 2^n。
由于奇数平方模8余1,故a^b=a mod 8, b^a=b mod 8
故a=b mod 8
由于奇数四次方模16余1,故a^b=a^(b%4) mod 16, b^a=b^(a%4) mod 16
由于b%4=a%4,故a=b mod 16
以此类推,得b=a mod 2^n。解唯一(当然打表可以看出来,现场一堆打表过的)
对于a为偶数的情况,b一定为偶数。
若b≥n,则a^b=0 mod 2^n,故b^a=0 mod 2^n。
可直接求出b≥n时的个数。
若b<n,则直接暴力,时间复杂度O(nloga)。
参考链接:
HDU - 6189 Law of Commutation(找规律+推公式 17广西邀请赛题)
HDU 6189 Law of Commutation 2017ACM-ICPC 广西邀请赛 (打表找规律)
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef unsigned long long ll;
const long long mpd = 1e9+7;
ll m;
ll quickmi( ll a, ll b ){
ll ans = 1;
while(b){
if(b&1) ans = (a*ans) % m;
b >>= 1;
a = (a*a) % m;
}
return ans;
}
ll qmi( ll a, ll b ){
ll ans = 1;
while(b){
if(b&1) ans = a*ans;
b >>= 1;
a = a*a;
}
return ans;
}
int main()
{
ll n, a;
while( ~scanf("%llu%llu",&n, &a) ){
if( a % 2 != 0 )
puts("1");
else{
m = 1 << n;
ll cnt = 0;
for( ll i = 1; i <= n; i++ ){
ll amod = quickmi(a, i) % m;
ll bmod = quickmi(i, a) % m;
if( amod == bmod )
cnt++;
}
ll temp = n / a;
if( temp*a < n ) temp += 1;
ll tmp = qmi(2, temp);
cnt += m/tmp - n/tmp;
printf("%llu\n",cnt);
}
}
return 0;
}