【知识总结】Polya 定理

我第一次听说 Polya 原理是 NOIP2017 以前,但我觉得太难想着以后再学;

NOIP2018 以前我觉得会考这玩意,下定决心学,后来咕了;

WC2019 以前我觉得会考这玩意,下定决心学,后来咕了;

SNOI2019 以前我觉得会考这玩意,下定决心学,后来咕了;

CTS2019 以前我觉得会考这玩意,下定决心学,后来咕了;

APIO2019 以前我觉得会考这玩意,下定决心学,后来咕了;

THUSC2019 以前我觉得会考这玩意,下定决心学,后来咕了;

今天距离 NOI2019 还有不到一周,我又下定决心学,赶紧抓着 jumpmelon 让他给我讲,然后他两句话就说完了。

摔.jpg

由于 Polya 原理是我临时抱瓜脚学的,所以这篇博客里只有不严谨的结论,严谨了表述和证明什么的 NOI 以后 看心情 再补。

基于上述原因,这篇博客的吐槽环节可能比正文(不含代码)还长

问题:有一个 \(n\) 个点的环和 \(m\) 种颜色,求给每一个点染一种颜色的本质不同的方案数。两种方案本质不同当且仅当它们不能通过旋转互相得到。

结论:方案数是 \(\frac{1}{n}\sum_{i=1}^n m^{\gcd(n,i)}\)

例题:洛谷 4980 Polya 定理

直接套路地推式子就好啦:

\[ \begin{aligned} ans&=\frac{1}{n}\sum_{i=1}^n n^{\gcd(n,i)}\\ &=\frac{1}{n}\sum_{d|n}n^d\sum_{i=1}^{\frac{n}{d}}[gcd(\frac{n}{d},i)=1]\\ &=\frac{1}{n}\sum_{d|n}n^d\varphi(\frac{n}{d}) \end{aligned}\]

\(n\) 分解质因子然后按照质因子搜索它的所有因数,搜索时维护一下 \(\varphi\) 就好了

代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    typedef long long ll;
    typedef pair<int, int> pii;
    const int SQRTN = 4e4 + 10, P = 1e9 + 7;
    pii prime[SQRTN];
    int pcnt, n, ans;
    int power(int a, int b)
    {
        int ans = 1;
        while (b)
        {
            if (b & 1)
                ans = (ll)ans * a % P;
            a = (ll)a * a % P;
            b >>= 1;
        }
        return ans;
    }
    int inv(const int a)
    {
        return power(a, P - 2);
    }
    void get_prime(int n)
    {
        pcnt = 0;
        for (int i = 2; i * i <= n; i++)
            if (n % i == 0)
            {
                prime[pcnt++] = pii(i, 0);
                while (n % i == 0)
                    ++prime[pcnt - 1].second, n /= i;
            }
        if (n > 1)
            prime[pcnt++] = pii(n, 1);
    }
    void dfs(const int pos, const int mul, const int pmul, const int div)
    {
        if (pos == pcnt)
        {
            ans = (ans + (ll)mul / div * pmul * power(n, n / mul)) % P;
            return;
        }
        dfs(pos + 1, mul, pmul, div);
        for (int i = 1, tmp = prime[pos].first; i <= prime[pos].second; i++, tmp *= prime[pos].first)
            dfs(pos + 1, mul * tmp, pmul * (prime[pos].first - 1), div * (prime[pos].first));
    }
    int work()
    {
        int T;
        read(T);
        while (T--)
        {
            ans = 0;
            read(n);
            get_prime(n);
            dfs(0, 1, 1, 1);
            write((ll)ans * inv(n) % P), putchar('\n');;
        }
        return 0;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("4980.in", "r", stdin);
#endif
    return zyt::work();
}

猜你喜欢

转载自www.cnblogs.com/zyt1253679098/p/11161765.html