P4980 【模板】Polya定理

https://www.luogu.org/problem/P4980

题意:很简单的,就一个最简单的polya定理。

做法:网上随便看看吧,写得都不错。

我们要求的就是上面的式子,我们容易知道|G|=n,|B|=n;

但不过重点就是c(g);

但不动的时候 c(g)=n这点没有毛病吧,因为都没有动。

但动k个点时候,这个时候就要看k了,随便找一找规律就知道这个就是 gcd(n,i)。

如果这个不知道,就仔细看看polya定理,并且看看手推一下简单样例。

ans=\frac{1}{n}\sum_{i=1}^{n}n^{gcd(n,i)}=\frac{1}{n}\sum_{p|n}n^{p}*\varphi(\frac{n}{p})

然后随便处理一下就可以了。注意答案会爆long long

#include "bits/stdc++.h"

using namespace std;
const int maxn = 300000 + 10;
//typedef long long ll;
typedef __int128 ll;
const ll mod = 1000000000 + 7;
ll quick(ll a, ll n, ll p) {
    ll ans = 1;
    for (; n; n >>= 1, a = a * a % p)
        if (n & 1) ans = ans * a % p;
    return ans;
}
void print(__int128 x) {
    if (!x) {
        puts("0");
        return;
    }
    string ret = "";
    while (x) {
        ret += x % 10 + '0';
        x /= 10;
    }
    reverse(ret.begin(), ret.end());
    cout << ret << endl;
}

int pri[maxn], phi[maxn], vis[maxn], cnt;
void init() {
    vis[1] = phi[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) {
            pri[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt && i * pri[j] < maxn; j++) {
            vis[i * pri[j]] = 1;
            if (i % pri[j] == 0) {
                phi[i * pri[j]] = phi[i] * pri[j];
                break;
            }
            phi[i * pri[j]] = phi[i] * (pri[j] - 1);
        }
    }
}
//unordered_map<ll, ll> mp;
ll euler(ll n) {
    if (n < maxn) return phi[n];
//    if (mp.count(x)) return mp[n];
    ll ret = 1;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            n /= i, ret *= (i - 1);
            while (n % i == 0) n /= i, ret *= i;
        }
    }
    if (n > 1) ret *= (n - 1);
    return ret;
}

ll solve(long long n) {
    ll ans = 0;
    for (ll i = 1; i * i <= n; i++) {
        if (n % i != 0) continue;
        ans += quick(n, i - 1, mod) * euler(n / i) % mod;
        ans %= mod;
        if (i * i == n) continue;
        if (n % i == 0) {
            ans += quick(n, n / i - 1, mod) * euler(i) % mod;
            ans %= mod;
        }
    }
    return ans;
}

int main() {
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    init();
    int T;
    long long n;
    cin >> T;
    while (T--) {
        cin >> n;
        print(solve(n));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值