zoukankan      html  css  js  c++  java
  • [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 {2pi}{n} + isin frac {2pi}{n}) ]

    (换句话说,让你求出一些分圆多项式)

    题解

    (f(n) = x ^ n - 1)(g(d) = Phi_d(x)),已知

    [f(n) = prod_{d | n}g(d) ]

    通过莫比乌斯反演,可以得到

    [egin{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 imes 3 imes 5 imes 7 imes 11 imes 13 imes 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))

    由于数据保证(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('
    ');
    	}
    	return 0;
    }
    

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

  • 相关阅读:
    批量备份mysql数据库(shell编程)
    批量检查多个网址是否正常(shell编程)
    就linux三剑客简单归纳
    sql语句浅谈以及mysql遇到的问题解决见解
    linux shell每天一阅 -- 安装nginx以及apache
    Linux文件系统检查错误
    Linux账号管理和ACL
    简书博客
    Block内的强引用
    一次没有意义的优化
  • 原文地址:https://www.cnblogs.com/psimonw/p/10727406.html
Copyright © 2011-2022 走看看