luogu P4725 多项式对数函数 (模板题、FFT、多项式求逆、求导和积分)

手动博客搬家: 本文发表于20181125 13:25:03, 原地址https://blog.csdn.net/suncongbo/article/details/84487306

题目链接: https://www.luogu.org/problemnew/show/P4725

题目大意: 给定一个\(n\)次多项式\(A(x)\), 求一个\(n\)次多项式\(B(x)\)满足\(B(x)\equiv \ln A(x) (\mod x^n)\)

题解: 神数学模板题……

数学真奇妙!

前驱知识

导数、积分相关

幂函数的求导

\(f(x)=x^n, f‘(x)=nx^{n-1}\)

和的导数等于导数的和

\((f+g)‘(x)=f‘(x)+g‘(x)\)

一般多项式的求导

\(f(x)=\sum^{n-1}_{i=0} a_ix^i, f‘(x)=\sum^{n-2}_{i=0} (i+1)a_{i+1}x^i\)

对数函数\(\ln\)的求导

\(f(x)=\ln(x), f‘(x)=\frac{1}{x}\)

复合函数求导——链式法则

\(f(g(x))‘=f‘(g(x))g‘(x)\)

求导的逆运算——积分

本题解法

\(g(x)\equiv \ln f(x) (\mod x^n)\)

两边同时求导可得

\(g‘(x)\equiv \frac{f‘(x)}{f(x)} (\mod x^n)\)

结束!

多项式求逆算\(\frac{1}{f(x)}\),再和\(f‘(x)\)相乘即可得到\(g‘(x)\)。

(多项式求逆见蒟蒻一篇博客 https://blog.csdn.net/suncongbo/article/details/84485718)

\(g‘(x)\)积个分得到\(g(x)\). 常数项,直接为\(0\).

时间复杂度\(O(n\log n)\)

常数,我写的大概\(9\)倍吧,求逆是\(6\)倍,再做个乘法就是\(3\)倍。

UPD: 仔细想了一下我这个常数好像是\(18\)倍(见我多项式求逆那篇博客)

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define llong long long
#define ldouble long double
#define uint unsigned int
#define ullong unsigned long long
#define udouble unsigned double
#define uldouble unsigned long double
#define modinc(x) {if(x>=P) x-=P;}
#define pii pair<int,int>
#define piii pair<pair<int,int>,int>
#define piiii pair<pair<int,int>,pair<int,int> >
#define pli pair<llong,int>
#define pll pair<llong,llong>
#define Memset(a,x) {memset(a,x,sizeof(a));}
using namespace std;

const int N = 1<<19;
const int P = 998244353;
const int LGN = 19;
const int G = 3;
llong a[N+3];
llong b[N+3];
llong tmp1[N+3],tmp2[N+3],tmp3[N+3],tmp4[N+3]; //inv
llong tmp7[N+3],tmp8[N+3],tmp9[N+3],tmp10[N+3]; //ln
int id[N+2];
int n;

void initid(int _len)
{
 id[0] = 0;
 for(int i=1; i<(1<<_len); i++) id[i] = (id[i>>1]>>1)|((i&1)<<(_len-1));
}

llong quickpow(llong x,llong y)
{
 llong cur = x,ret = 1ll;
 for(int i=0; y; i++)
 {
  if(y&(1ll<<i))
  {
   y-=(1ll<<i); ret = ret*cur%P;
  }
  cur = cur*cur%P;
 }
 return ret;
}
llong mulinv(llong x) {return quickpow(x,P-2);}

void ntt(int dgr,int coe,llong poly[],llong ret[])
{
 int len = 0; for(int i=0; i<=LGN; i++) if((1<<i)==dgr) {len = i; break;}
 initid(len); for(int i=0; i<dgr; i++) ret[i] = 0ll;
 for(int i=0; i<dgr; i++) ret[i] = poly[i];
 for(int i=0; i<dgr; i++) if(i<id[i]) swap(ret[i],ret[id[i]]);
 for(int i=1; i<=(dgr>>1); i<<=1)
 {
  llong tmp = quickpow(G,(P-1)/(i<<1));
  if(coe==-1) tmp = mulinv(tmp);
  for(int j=0; j<dgr; j+=(i<<1))
  {
   llong expn = 1ll;
   for(int k=0; k<i; k++)
   {
    llong x = ret[j+k],y = (expn*ret[j+i+k])%P;
    ret[j+k] = x+y; modinc(ret[j+k]);
    ret[j+i+k] = x-y+P; modinc(ret[j+i+k]);
    expn = (expn*tmp)%P;
   }
  }
 }
 if(coe==-1)
 {
  llong tmp = mulinv(dgr);
  for(int i=0; i<dgr; i++) ret[i] = ret[i]*tmp%P;
 }
}

void polyinv(int dgr,llong poly[],llong ret[])
{
 for(int i=0; i<dgr; i++) ret[i] = 0ll;
 ret[0] = mulinv(poly[0]);
 for(int i=1; i<=(dgr>>1); i<<=1)
 {
  for(int j=0; j<(i<<2); j++) tmp1[j] = j<i ? ret[j] : 0ll;
  for(int j=0; j<(i<<2); j++) tmp2[j] = j<(i<<1) ? poly[j] : 0ll;
  ntt((i<<2),1,tmp1,tmp3); ntt((i<<2),1,tmp2,tmp4);
  for(int j=0; j<(i<<2); j++) tmp3[j] = tmp3[j]*tmp3[j]%P*tmp4[j]%P;
  ntt((i<<2),-1,tmp3,tmp4);
  for(int j=0; j<(i<<1); j++) ret[j] = (tmp1[j]+tmp1[j]-tmp4[j]+P)%P;
 }
 for(int i=dgr; i<(dgr<<1); i++) ret[i] = 0ll;
}

void polyder(int dgr,llong poly[],llong ret[])
{
 for(int i=0; i<dgr-1; i++) ret[i] = poly[i+1]*(i+1)%P;
}

void polyint(int dgr,llong poly[],llong ret[])
{
 for(int i=1; i<=dgr; i++) ret[i] = poly[i-1]*mulinv(i)%P;
}

void polyln(int dgr,llong poly[],llong ret[])
{
 polyder(dgr,poly,tmp7);
 polyinv(dgr,poly,tmp8);
 ntt((dgr<<1),1,tmp8,tmp9); ntt((dgr<<1),1,tmp7,tmp10);
 for(int i=0; i<(dgr<<1); i++) tmp9[i] = tmp9[i]*tmp10[i]%P;
 ntt((dgr<<1),-1,tmp9,tmp10);
 polyint(dgr,tmp10,ret);
}

int main()
{
 scanf("%d",&n); int dgr = 1; while(dgr<=n) dgr<<=1;
 for(int i=0; i<n; i++) scanf("%lld",&a[i]);
 polyln(dgr,a,b);
 for(int i=0; i<n; i++) printf("%lld ",b[i]);
 return 0;
}

原文地址:https://www.cnblogs.com/suncongbo/p/10311227.html

时间: 2024-09-28 07:53:38

luogu P4725 多项式对数函数 (模板题、FFT、多项式求逆、求导和积分)的相关文章

【bzoj3456】城市规划 容斥原理+NTT+多项式求逆

题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为方案数 mod 1004535809. 样例输入 3 样例输出 4 题解 容斥原理+NTT+多项式求逆 设 $f_i$ 表示 $i$ 个点的简单无向连通图的数目,$g_i$ 表示 $i$ 个点的简单无向图的数目. 根据定义得 $g_i=2^{\frac{n(n-1}2}$ . 对于 $f_i$ ,考虑容斥

多项式求逆

多项式求逆 求 \(A(x)\) 在 \(\%x^{n}\) 意义下的逆元 \(B(x)\) 首先求出 \(A(x)\) 在 \(\%x^{\lceil \frac{n}{2} \rceil}\) 意义下的逆元 \(C(x)\),即 $A(x)C(x)=1 $ \((\%x^{\lceil \frac{n}{2} \rceil})\) 移项得 \(A(x)C(x)-1 = 0\) \((\%x^{\lceil \frac{n}{2} \rceil})\) 两边平方 \(A^{2}(x)C^{2}

P4725 【模板】多项式对数函数

\(\color{#0066ff}{ 题目描述 }\) 给出 \(n-1\) 次多项式 \(A(x)\),求一个 \(\bmod{\:x^n}\)下的多项式 \(B(x)\),满足 \(B(x) \equiv \ln A(x)\) 在 \(\text{mod } 998244353\)下进行,且 \(a_i \in [0, 998244353] \cap \mathbb{Z}\) \(\color{#0066ff}{输入格式}\) 第一行一个整数 \(n\). 下一行有 \(n\) 个整数,依次

Luogu 4725 【模板】多项式对数函数

继续补全模板. 要求 $$g(x) = ln f(x)$$ 两边求导, $$g'(x) = \frac{f'(x)}{f(x)}$$ 然后左转去把多项式求导和多项式求逆的模板复制过来,就可以计算出$g'(x)$,接下来再对$g'(x)$求不定积分即可. 虽然我也不是很会不定积分,但是这就是求导的逆过程,相当于把求完导之后的函数搞回去. 因为$(a_ix^i)' = ia_ix^{i - 1}$,所以反向算一下就好. 求导的时间复杂度是$O(n)$,积分的时间复杂度是$O(nlogn)$,总时间复

多项式FFT/NTT模板(含乘法/逆元/log/exp/求导/积分/快速幂)

自己整理出来的模板 存在的问题: 1.多项式求逆常数过大(尤其是浮点数FFT) 2.log只支持f[0]=1的情况,exp只支持f[0]=0的情况 有待进一步修改和完善 FFT: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef double db; 5 const db pi=acos(-1); 6 const int N=4e5+10,M=1e6+10,mod=9982443

[学习笔记] 多项式与快速傅里叶变换(FFT)基础

引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积(或者多项式乘法/高精度乘法), 而代码量却非常小. 博主一年半前曾经因COGS的一道叫做"神秘的常数 $\pi$"的题目而去学习过FFT, 但是基本就是照着板子打打完并不知道自己在写些什么鬼畜的东西OwO 不过...博主这几天突然照着算法导论自己看了一遍发现自己似乎突然意识到了什么OwO然后就打了一道板子题还1A了OwO再加上午考试差点AK

FFT多项式乘法学习笔记

??其实我不知道我是否真的理解了FFT,但是我会用FFT优化多项式乘法了QAQ.. (以下大多摘自算导 前置知识 1. 多项式 ??在一个代数域F上,关于变量x的多项式定义为形式和形式表示的函数 A(x)=∑j=0n?1ajxj,其中a0-an?1为多项式各项的系数 2. 多项式的次数界 ??若多项式有非零系数的最高次项为xk,则称k为该多项式的次数,任何严格大于k的整数都是这个多项式的次数界. 3. 多项式的表示 (1)系数表示法 ??对于一个次数界为n的多项式A(x)来说,其系数表示法可以看

Luogu 2801 教主的魔法 | 分块模板题

Luogu 2801 教主的魔法 | 分块模板题 我犯的错误: 有一处l打成了1,还看不出来-- 缩小块大小De完bug后忘了把块大小改回去就提交--还以为自己一定能A了-- #include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef long long ll; #define space putchar(' ')

【BZOJ 2179】【FFT模板】 FFT快速傅立叶

2179: FFT快速傅立叶 Time Limit: 10 Sec Memory Limit: 259 MB Submit: 1595 Solved: 792 [Submit][Status][Discuss] Description 给出两个n位10进制整数x和y,你需要计算x*y. Input 第一行一个正整数n. 第二行描述一个位数为n的正整数x. 第三行描述一个位数为n的正整数y. Output 输出一行,即x*y的结果. Sample Input 1 3 4 Sample Output