zoukankan      html  css  js  c++  java
  • 【NFLSPC #2】Polynomial【多项式】

    以下所有值均在 (mathbb F_{998244353}) 上运算。

    给定多项式 (P(x)=x)(n) 次首 (1) 多项式 (Q(x)=sum_{k=0}^n a_kx^k),你可以进行两种操作:

    • 1 c:表示令 (P(x):=P(x)+c)
    • 2 k:表示令 (P(x):=P(x)^k)

    构造长度最小的操作序列,使 (P(x)=Q(x))。需判断无解。

    (nle 10^6)


    显然最终的操作序列会使 (P(x)=(cdots(x^{k_0}+c_1)^{k_1}+cdots+c_m)^{c_m}),其中 (k_0) 可以 (=1),其他的 (k_i>1),所有 (c_i e 0)

    考虑将操作改为对 (Q(x)) 做,则 1 c 表示 (Q(x):=Q(x-c))2 k 表示令 (Q(x):=Q(x^{1/k}))

    先看 (k_0=1) 的情况,归纳一下可以得到 (c_1=[x^{n-1}]Q(x)/n),于是直接平移一下即可。然后将 (Q(x)) 的非 (0) 项的次数除以它们的 (gcd)

    (k_0 e 1),初始时判一下 (gcd) 即可。若有某一步缩不起来就无解。

    每次除以 (gcd) 次数至少减半,于是得到时间复杂度 (T(n)=T(n/2)+O(nlog n)=O(nlog n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 1<<21, mod = 998244353;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0; bool f = false;
    	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    } int ksm(int a, int b){
    	int res = 1;
    	for(;b;b >>= 1, a = (LL)a * a % mod)
    		if(b & 1) res = (LL)res * a % mod;
    	return res;
    } void qmo(int &x){x += x >> 31 & mod;}
    int n, m, lim, rev[N], w[N], a[N], b[N], fac[N], finv[N], inv[N], ans[50];
    int gcd(int x, int y){return y ? gcd(y, x % y) : x;}
    int calc(){ int g = 0;
    	for(int i = 1;i <= n && g != 1;++ i)
    		if(a[i]) g = gcd(g, i);
    	return g;
    } void work(int g){
    	assert(!(n % g)); n /= g; ans[m++] = -g;
    	for(int i = 0;i <= n;++ i){b[i] = a[i * g]; a[i * g] = 0;}
    	memcpy(a, b, n+1<<2); memset(b, 0, n+1<<2);
    } void init(int n){
    	fac[0] = 1;
    	for(int i = 1;i <= n;++ i) fac[i] = (LL)fac[i-1] * i % mod;
    	finv[n] = ksm(fac[n], mod-2);
    	for(int i = n;i;-- i){
    		finv[i-1] = (LL)finv[i] * i % mod;
    		inv[i] = (LL)finv[i] * fac[i-1] % mod;
    	} for(int mid = 1;mid < N;mid <<= 1){
    		int Wn = ksm(3, (mod-1) / (mid<<1)); w[mid] = 1;
    		for(int i = 1;i < mid;++ i) w[mid+i] = (LL)w[mid+i-1] * Wn % mod;
    	}
    } void calrev(int len){
    	int L = -1; lim = 1;
    	while(lim <= len){lim <<= 1; ++ L;}
    	for(int i = 0;i < lim;++ i)
    		rev[i] = (rev[i>>1]>>1) | ((i&1)<<L);
    } void NTT(int *A, bool op){
    	for(int i = 0;i < lim;++ i)
    		if(i < rev[i]) swap(A[i], A[rev[i]]);
    	for(int mid = 1;mid < lim;mid <<= 1)
    		for(int i = 0;i < lim;i += mid<<1)
    			for(int j = 0;j < mid;++ j){
    				int y = (LL)((op && j) ? (mod - w[(mid<<1)-j]) : w[mid+j]) * A[mid+i+j] % mod;
    				qmo(A[mid+i+j] = A[i+j] - y); qmo(A[i+j] += y - mod); 
    			}
    	if(op){ int inv = ksm(lim, mod-2);
    		for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * inv % mod; 
    	}
    }
    int main(){
    	read(n); init(n);
    	for(int i = 0;i <= n;++ i) read(a[i]);
    	int g = calc(); if(g != 1) work(g);
    	while(n > 1){
    		if(!a[n-1]) return puts("-1"), 0;
    		int c = (LL)a[n-1] * inv[n] % mod; ans[m++] = c; c = mod - c;
    		for(int i = 0;i <= n;++ i) a[i] = (LL)a[i] * fac[i] % mod;
    		for(int i = 0, pw = 1;i <= n;++ i, pw = (LL)pw * c % mod) b[n-i] = (LL)pw * finv[i] % mod;
    		calrev(n<<1); NTT(a, 0); NTT(b, 0);
    		for(int i = 0;i < lim;++ i) a[i] = (LL)a[i] * b[i] % mod;
    		NTT(a, 1); for(int i = 0;i <= n;++ i) a[i] = (LL)a[i+n] * finv[i] % mod;
    		memset(a+n+1, 0, lim-n-1<<2); memset(b, 0, lim<<2);
    		int g = calc(); if(g == 1) return puts("-1"), 0; work(g);
    	} if(a[0]) ans[m++] = a[0];
    	printf("%d
    ", m);
    	for(int i = 0;i < m;++ i) printf("%d %d
    ", 1 + (ans[i] < 0), abs(ans[i]));
    }
    
  • 相关阅读:
    警匪游戏规则
    敏捷开发流程总结
    天际PRO-CR16 改装方案
    世界时间(卡西欧电波表24个城市缩写翻译及简介)
    jmeter 获取总的线程数
    linux 重启jmeter服务
    jmeter 生成不重复的手机号
    Python和Java两门编程语言,学习哪个更好?
    JAVA和前端该选哪个?
    2020年Java程序员的就业前景如何?
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14604770.html
Copyright © 2011-2022 走看看