zoukankan      html  css  js  c++  java
  • 洛谷P6800 【模板】Chirp-Z Transform 题解

    (ans_i = F(c^i) = sumlimits_{j=0}^{n-1} a_jc^{ij})

    众所周知, (ij = inom{i+j}{2} - inom{i}{2} - inom{j}{2}) :

    (ans_i = c^{-inom{i}{2}} sumlimits_{j=0}^{n-1} a_jc^{-inom{j}{2}} c^{inom{i+j}{2}})

    不难发现这是一个卷积形式,模数 (998244353) 为 NTT 模数, NTT 即可。

    几个细节:

    (1.) 注意到我们是把一个长度为 (n+m) 的多项式和 长度为 (n) 的多项式做卷积,但是我们只需要卷积结果的第 (n - (n+m-1)) 项,由于 NTT 是循环卷积,所以我们可以把多项式长度设为 (n+m) , 对答案没有影响。

    (2.) 每次用快速幂算 (c^{-inom{i}{2}})(c^{inom{i}{2}})(Theta((n+m)log mod)) 的,非常慢。

    可以 (Theta(sqrt{mod})) 预处理然后光速幂 (Theta(1)) 查询,或者用两次前缀积直接递推结果.

    code(最慢的点324ms) :

    #include <bits/stdc++.h>
    #define LL unsigned long long
    #define uint unsigned int
    using namespace std;
    template <typename T> void read(T &x){
    	static char ch; x = 0,ch = getchar();
    	while (!isdigit(ch)) ch = getchar();
    	while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    const int P = 998244353,N = 1<<21;
    inline uint power(uint x,int y){
    	y %= P-1;
    	static uint r; r = 1; while (y){ if (y&1) r = (LL)r * x % P; x = (LL)x * x % P,y >>= 1; }
    	return r;
    }
    int n,m,c,R[N],L; uint a[N],w[N<<1],iw[N<<1],pwc[N],ipwc[N],F[N],G[N];
    inline int getR(int n){
    	int i,l = 1,Lim = 2; while (Lim <= n) Lim <<= 1,++l;
    	for (i = 0; i < Lim; ++i) R[i] = (R[i>>1]>>1) | ((i&1)<<l-1);
    	return Lim;
    }
    inline void NTT(uint *A,int n){
    	register int i,j,k; uint v;
    	for (i = 1; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]);
    	for (i = 1; i < n; i <<= 1) for (j = 0; j < n; j += i << 1) for (k = j; k < i+j; ++k)
    		v = (LL)A[k+i] * w[(i<<1)+k-j] % P,A[k+i] = (A[k]<v)?(A[k]+P-v):(A[k]-v),A[k] = (A[k]+v>=P)?(A[k]+v-P):(A[k]+v);
    }
    inline void iNTT(uint *A,int n){
    	register int i,j,k; uint v;
    	for (i = 1; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]);
    	for (i = 1; i < n; i <<= 1) for (j = 0; j < n; j += i << 1) for (k = j; k < i+j; ++k)
    		v = (LL)A[k+i] * iw[(i<<1)+k-j] % P,A[k+i] = (A[k]<v)?(A[k]+P-v):(A[k]-v),A[k] = (A[k]+v>=P)?(A[k]+v-P):(A[k]+v);
    	for (i = 0,v = power(n,P-2); i < n; ++i) A[i] = (LL)A[i] * v % P;
    }
    int main(){
    	register int i,j; uint v;
    	read(n),read(c),read(m);
    	pwc[1] = c,ipwc[1] = power(c,P-2);
    	for (pwc[0] = i = 1; i <= n+m; ++i) pwc[i] = (LL)pwc[i-1] * pwc[1] % P;
    	for (pwc[0] = i = 1; i <= n+m; ++i) pwc[i] = (LL)pwc[i] * pwc[i-1] % P;
    	for (ipwc[0] = i = 1; i <= max(n,m); ++i) ipwc[i] = (LL)ipwc[i-1] * ipwc[1] % P;
    	for (ipwc[0] = i = 1; i <= max(n,m); ++i) ipwc[i] = (LL)ipwc[i] * ipwc[i-1] % P;
    	for (i = 0; i < n; ++i) read(a[i]),a[i] = (LL)a[i] * (i?ipwc[i-1]:1) % P;
    	L = getR(n+m);
    	for (i = 1,j = 2; j <= L; ++i,j <<= 1){
    		int l = j + (j>>1);
    		w[j] = 1,v = power(3,(P-1)/j);
    		for (register int k = j+1; k <= l; ++k) w[k] = (LL)w[k-1] * v % P;
    		iw[j] = 1,v = power(v,P-2);
    		for (register int k = j+1; k <= l; ++k) iw[k] = (LL)iw[k-1] * v % P;
    	}
    	for (F[0] = 1,i = 1; i <= n+m; ++i) F[i] = pwc[i-1];
    	for (i = 1; i <= n; ++i) G[i] = a[n-i];
    	NTT(F,L); NTT(G,L);
    	for (i = 0; i < L; ++i) F[i] = (LL)F[i] * G[i] % P;
    	iNTT(F,L);
    	for (i = 0; i < m; ++i) write((LL)F[i+n] * (i?ipwc[i-1]:1) % P),putchar(i<m?' ':'
    ');
    	return 0;
    }
    
  • 相关阅读:
    自定义类型中结构体、枚举等,结构体内存对齐相关要点总结
    可变参数模拟printf()函数实现一个my_print()函数以及调用可变参数需注意的陷阱
    一些关于memcpy memmove函数的区别,和模拟实现
    浅谈 数组指针 指针数组 函数指针 函数指针数组... 以及它们之间区别。
    C语 三子棋小游戏
    5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果
    stm32移植ds18b20时出现的问题
    自己实现strcmp
    C语言新手写扫雷攻略3
    C语言新手写扫雷攻略2
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13770135.html
Copyright © 2011-2022 走看看