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 
#include 
#include 

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;
}

你可能感兴趣的:(思路,递推\规律)