SPOJ Transposing is Even More Fun

直接交换需要的次数为 $2^{a+b}$,如果有 $k$ 个循环,每个循环元素为 $x$ 个,只需要交换 $x-1$ 次,那么最后次数就是 $2^{a+b}-k$,本质就是求有多少个循环。
一个位置 $(x,y)$ 看成二进制的形式。
例如 $a=3, b=2$,位置 $(3,2)$ 看成二进制就是 $011 10$,交换后就是 $10011$,右移了 $b=2$ 位,那么就是求 $01$ 串在右移 $b$ 位同构的情况下有多少种本质不同串。
右移 $b$ 位有 $g=\gcd(a + b,b)$ 个循环,每个循环长度为 $G= \frac{a+b}{g}$,那么现在可以看成有 $G$ 元环,旋转等价,可以染成 $2^g$ 种颜色求方案数。

#include <bits/stdc++.h>

namespace IO {
    void read() {}
    template<class T, class ... T2>
    inline void read(T &x, T2 &... oth) {
        x = 0; T f = 1; char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
        x *= f;
        read(oth...);
    }
}

const int N = 1e6 + 7, MOD = 1000003;
int prime[N], prin, phi[N], bin[N];
bool vis[N];

int gcd(int a, int b) {
    while (b) {
        a %= b;
        std::swap(a, b);
    }
    return a;
}

int qp(int a, int b = MOD - 2) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1LL * a * ans % MOD;
        a = 1LL * a * a % MOD;
        b >>= 1;
    }
    return ans;
}

void init() {
    phi[1] = 1;
    for (int i = bin[0] = 1; i < N; i++) 
        bin[i] = 2LL * bin[i - 1] % MOD;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            prime[++prin] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= prin && i * prime[j] < N; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
        }
    }
}

void M(int &a) {
    if (a >= MOD) a -= MOD;
    if (a < 0) a += MOD;
}

int solve(int n, int m) {
    int ans = 0;
    for (int i = 1; i * i <= n; i++) {
        if (n % i) continue;
        M(ans += 1LL * phi[n / i] * bin[i * m] % MOD);
        if (i * i != n)
            M(ans += 1LL * phi[i] * bin[n / i * m] % MOD);
    }
    return 1LL * ans * qp(n) % MOD;
}

int main() {
    init();
    int T;
    IO::read(T);
    while (T--) {
        int a, b;
        IO::read(a, b);
        if (!a || !b) {
            puts("0");
            continue;
        }
        int g = gcd(a, b);
        int n = (a + b) / g, m = g;
        int ans;
        M(ans = bin[a + b] - solve(n, m));
        printf("%d\n", ans);
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/Mrzdtz220/p/12232282.html