[hdu 6352] Call It What You Want

题意

给出若干组询问,每次询问一个\(n\),求将\(x ^ n - 1\)分解为分圆多项式的乘积。

具体的,即
\[ x ^ n - 1 = \prod_{d | n} \Phi_d(x) \]
其中分圆多项式的定义为
\[ \Phi_n(x) = \prod_{1 \le k \le n, (n, k) = 1} (x - w_n ^ k) \quad (w_n = \cos \frac {2\pi}{n} + i\sin \frac {2\pi}{n}) \]
(换句话说,让你求出一些分圆多项式)

题解

\(f(n) = x ^ n - 1\)\(g(d) = \Phi_d(x)\),已知
\[ f(n) = \prod_{d | n}g(d) \]
通过莫比乌斯反演,可以得到
\[ \begin{aligned} g(n) & = \prod_{d | n} f(d) ^ {\mu(\frac {n}{d})} \\ & = \prod_{d | n} f(\frac {n}{d}) ^ {\mu(d)} \\ \end{aligned} \]
注意到我们只需要\(\mu(d)\)不为\(0\)\(d\),且满足的\(d\)都无平方质因子。

注意到\(d \le n \le 10 ^ 5 < 2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17\),我们只要用\(2 ^ 6\)枚举每个有用的\(d\)即可。那么因子\(d\)\(g(n)\)的贡献项为\((1 - x ^ {\frac {n}{d}}) ^ {\mu(d)}\)

如果用多项式乘法把他们搞起来当然不行,复杂度爆炸。

我考虑这个多项式只有两项有值,可以通过一个巧妙的dp(递推)进行计算。

具体来说,

如果乘上的是\((1 - x ^ k) ^ 1\),可以直接用递推来更新系数(本质是01背包);

如果乘上的是\((1 - x ^ k) ^ {-1}\),亦即\((1 + x ^ k + x ^ {2k} + ...)\),可以用类似无限背包的递推来累加系数。

由于\(\Phi_d(x)\)的次数是\(\phi(d)\),那么这个部分只需要用\(O(\phi(d))\)的复杂度计算即可。

则总复杂度为\(O(\sum_{d | n} 2 ^ \omega(d) \phi(d)) = O(n2 ^ 6)\)

扫描二维码关注公众号,回复: 5930693 查看本文章

由于数据保证\(\sum n \le 5 \cdot 10 ^ 6\),所以总复杂度大概是\(O(5 \cdot 10 ^ 6 \cdot 2 ^ 6)\)

#include <bits/stdc++.h>
using namespace std;

inline int read () {
    static int x;
    scanf("%d", &x);
    return x;
}

const int N = 1e5 + 5;
int n, m;
int phi[N], pr[N][7];
int id[N]; bool vis[N];
vector <int> poly[N];
void prework () {
    phi[1] = 1;
    for (int i = 2; i < N; ++i) {
        if (!pr[i][0])
            for (int j = i; j < N; j += i)
                pr[j][++pr[j][0]] = i;
        phi[i] = i;
        for (int j = 1; j <= pr[i][0]; ++j)
            phi[i] -= phi[i] / pr[i][j];
    }
}
bool cmp (int u, int v) {
    if (phi[u] != phi[v]) return phi[u] < phi[v];
    for (int i = phi[u]; ~i; --i) if (poly[u][i] != poly[v][i])
        return poly[u][i] < poly[v][i];
    return 0;
}
signed main () {
    prework();
    for (int _ = read(); _; --_) {
        n = read(), m = 0;
        for (int i = 1; i <= n; ++i) if (n % i == 0) {
            id[++m] = i;
            if (vis[i]) continue;
            poly[i].resize(phi[i] + 1, 0);
            poly[i][0] = i > 1 ? 1 : -1;
            for (int j = 0; j < (1 << pr[i][0]); ++j) {
                int d = i, s = 1;
                for (int k = 0; k < pr[i][0]; ++k)
                    if (j >> k & 1) d /= pr[i][k + 1], s = -s;
                if (s > 0)
                    for (int j = phi[i]; j >= d; --j)
                        poly[i][j] -= poly[i][j - d];
                else
                    for (int j = d; j <= phi[i]; ++j)
                        poly[i][j] += poly[i][j - d];
            }
            vis[i] = 1;
        }
        sort(id + 1, id + 1 + m, cmp);
        for (int i = 1, x; i <= m; ++i) {
            x = id[i];
            putchar('(');
            for (int j = phi[x], f = 1; ~j; --j, f = 0) if (poly[x][j]) {
                if (poly[x][j] < 0) putchar('-');
                else if (!f) putchar('+');
                if (!j || abs(poly[x][j]) != 1) printf("%d", abs(poly[x][j]));
                if (j) printf("x");
                if (j > 1) printf("^%d", j);
            }
            putchar(')');
        }
        putchar('\n');
    }
    return 0;
}

(输出复杂度可能是个瓶颈,本地测是输不出来的,但提交后跑得很快,AC)。

猜你喜欢

转载自www.cnblogs.com/psimonw/p/10727406.html