多项式学习

FFT

单位根
点值表示DFT + IDFT

  1. 补0:补成 2 2 2 2 n 2n 2n次多项式
  2. 求值:用FFT计算 f 1 = D F T ( v 1 ) , f 2 = D F T ( v 2 ) f_1=DFT(v_1),f_2=DFT(v_2) f1=DFT(v1),f2=DFT(v2).求的是点值
  3. 乘法:把两个向量乘起来
  4. 差值:计算 I D F T ( f ) IDFT(f) IDFT(f)

FFT模板带优化(利用DFT的对称性)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define REP(i, a, b) for(int i = a; i <= b; i++)
#define PER(i, a, b) for(int i = a; i >= b; i--)
#define LL long long
inline int read() {
    int x = 0, flag = 1;char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') flag = - 1;
        ch = getchar();
    }
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x * flag;
}

const int N = 3e6 + 6;
int n, m, k, res = 0, ans[N], a, b;
int lim = 1, l, R[N];
const double Pi = acos(-1.0);
struct Complex {
    double x, y;
    Complex (double xx = 0, double yy = 0) {
        x = xx, y = yy;
    }
}A[N], B[N];

Complex operator * (Complex A, Complex B) {
    return Complex(A.x * B.x - A.y * B.y, A.x * B.y + A.y * B.x);
}

Complex operator + (Complex A, Complex B) {
    return Complex(A.x + B.x, A.y + B.y);
}

Complex operator - (Complex A, Complex B) {
    return Complex(A.x - B.x, A.y - B.y);
}

void FFT(Complex *C, double flag) {
    for(int i = 0; i < lim; ++i)
        if(i < R[i]) swap(C[i], C[R[i]]);
    for(int j = 1; j < lim; j <<= 1) {
        Complex T(cos(Pi / j), flag * sin(Pi / j));
        for(int k = 0; k < lim; k += (j << 1)) {
            Complex t(1, 0);
            for(int l = 0; l < j; ++l, t = t * T) {
                Complex Nx = C[k + l], Ny = t * C[k + j + l];
                C[k + l] = Nx + Ny;
                C[k + j + l] = Nx - Ny;
            }
        }
    }
}

int main() {   
    n = read(); m = read();
    for(int i = 0; i <= n; ++i) A[i].x = read();
    for(int i = 0; i <= m; ++i) B[i].x = read();
    while(lim <= n + m) lim <<= 1, l ++;
    for(int i = 0; i <= lim; ++i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (l - 1));
    FFT(A, 1); FFT(B, 1);
    for(int i = 0; i <= lim; ++i) A[i] = A[i] * B[i];
    FFT(A, -1);
    for(int i = 0; i <= n + m; ++i)
        printf("%d ", (int)(A[i].x / lim + 0.5));
    return 0;
}

NTT

取模意义下FFT
p=1004535809=479×2^21+1, 998244353
g=3
int t =pow(g,(mod-1)/(mid*2));//用原根代替单位根
if (inv==-1)temp=pow(temp,mod-2);//逆变换则乘上逆元

模数任意

面说了,要进行快速数论变换需要模数是 a⋅2k+1 形式的素数,但是在实际应用中,要求的模数可能不是这样的形式,甚至是一个合数!

假设现在需要模 m,并且进行变换的长度是 n
那么这样任何多项式系数的范围是 [0,m),两个相乘,不会超过 (m−1)2,一共 n 项相加,不会超过 n(m−1)2
这样的话,选取 k 个有上面形式的素数 p1,p2,⋯,pk,要求满足

∏i=1kpk>n(m−1)2
然后分别在 modk 的剩余系下做变换,最后使用中国剩余定理合并(当然这时候或许是需要高精度或者 __int128 的)

FWT

Ck = ∑i⊕j=k (Ai*Bj), ⊕是某种运算符号。当⊕是+时,即是傅里叶变换;当⊕是^, &, |等某种位运算时,即是FWT快速沃尔什变换。

FFT中,数组长度要大于结果的最高次幂,高位补0;FWT时,数组长度需要是2的整数次幂,不足补0。

原理不是怎么重要…

模板套用即可。

//快速沃尔什变换
void FWT(int*a,int n){
    for(int d=1;d<n;d<<=1)for(int m=d<<1,i=0;i<n;i+=m)for(int j=0;j<d;j++){
        int x=a[i+j],y=a[i+j+d];
        //xor:a[i+j]=x+y,a[i+j+d]=x−y;
        //and:a[i+j]=x+y;
        //or:a[i+j+d]=x+y;
    }
}
void UFWT(int*a,int n){
    for(int d=1;d<n;d<<=1)for(int m=d<<1,i=0;i<n;i+=m)for(int j=0;j<d;j++){
        int x=a[i+j],y=a[i+j+d];
        //xor:a[i+j]=(x+y)/2,a[i+j+d]=(x−y)/2;
        //and:a[i+j]=x−y;
        //or:a[i+j+d]=y−x;
    }
}

经典例题

UVa 12298

题目描述PDF
将每个花色写成多项式: A ( i ) , B ( i ) , C ( i ) , D ( i ) A(i),B(i),C(i),D(i) A(i),B(i),C(i),D(i)
规定每个花色每个点上的系数为0/1
0表示缺失、1表示存在
答案是 A ( i ) , B ( i ) , C ( i ) , D ( u ) A(i),B(i),C(i),D(u) A(i),B(i),C(i),D(u)三个多项式的卷积
注意使用long double

Codechef COUNTARI

给定 n n n以及长度为 n n n的数组 a i a_i ai,求有多少个 ( i , j , k ) (i,j,k) (i,j,k)满足 a j − a i = a k − a j ( i &lt; j &lt; k ) a_j-a_i=a_k-a_j(i&lt;j&lt;k) ajai=akaj(i<j<k)
数据范围 n ≤ 1 0 5 , a i ≤ 3 ∗ 1 0 4 n≤10^5,a_i≤3*10^4 n105,ai3104
首先考虑没有 i &lt; j &lt; k i&lt;j&lt;k i<j<k的限制,令 f ( x ) = ∑ i = 1 n x i a i f(x)=\sum_{i=1}^nx_i^{a_i} f(x)=i=1nxiai
2 a j = a i + a k 2a_j=a_i+a_k 2aj=ai+ak
固定 j j j后,合法的 ( i , k ) (i,k) (i,k)的数量为 f ( x ) ∗ f ( x ) [ x 2 a j ] f(x)*f(x)[x^{2a_j}] f(x)f(x)[x2aj]
现在加入大小限制,将序列以大小分为S分块

  1. i , j , k i,j,k i,j,k三个数至少有两个在第 b b b个块里,暴力枚举 O ( ( n S ) ∗ S 2 ) ; O((\frac{n}{S})*S^2); O((Sn)S2);
  2. j j j在第 b b b个块, S i Si Si在前 b − 1 b-1 b1个块, k k k在第 b + 1 b+1 b+1个块以后,结合没有限制的版本 O ( ( n 2 S ) l o g n ) O((\frac{n^2}{S})logn) O((Sn2)logn)
    设块的的大小 S = n l o g n S=\sqrt{nlogn} S=nlogn ,复杂度 O ( n n l o g n ) O(n\sqrt{nlogn}) O(nnlogn )

SRM603Hard Sum of arrays

Hero has two arrays of integers: A and B. Each of the arrays contains exactly n elements.
Hero will do two things with his arrays: First, he will permute the elements in each of his arrays somehow. (He is allowed to put the elements of each array into any order he likes, including their original order. He is not allowed to swap elements between A and B, each array has to be rearranged separately.) Afterwards, he will produce a new array C of n elements such that for all i, C[i] = A[i] + B[i].
意思就是给你两个数组 A [ i ] , B [ i ] A[i],B[i] A[i],B[i]你可以将两个数组进行排序,在对应位相加,使得新的数组 C [ i ] C[i] C[i]中众数出现的次数最多。
a [ i ] , b [ i ] a[i],b[i] a[i],b[i]为i在数组 A , B A,B A,B中出现的次数,答案是 m a x ∑ j = 1 n m i n ( a j , b i − j ) max\sum_{j=1}^nmin(a_j,b_{i-j}) maxj=1nmin(aj,bij)
a k [ i ] = [ a [ i ] &gt; = k ] a_k[i]=[a[i]&gt;=k] ak[i]=[a[i]>=k],则 c [ j ] = ∑ k &gt; = 1 ∑ i a k [ i ] ∗ b k [ i − j ] c[j]=\sum_{k&gt;=1}\sum_ia_k[i]*b_k[i-j] c[j]=k>=1iak[i]bk[ij]
设置阈值 t h th th,上面的式子只计算 ∑ k = 1 t h \sum_{k=1}^th k=1th,而 a [ i ] &gt; t h a[i]&gt;th a[i]>th的个数不超过 n t h \frac{n}{th} thn个,这部分枚举即可,复杂度复杂度 O ( t h ∗ n l o g n + ( n t h ) 2 ) O(th*nlogn+{(\frac{n}{th}})^2) O(thnlogn+(thn)2)

习题

模板:luogu 1919 luogu3803 luogu 4721 luogu4238 luogu4245 luogu4717 
HEOI/TJOI2016 求和、AHOI2017礼物、ZJOI2014力
AHOI2001多项式乘法 SDOI2015序列统计 SDOI2017序列计数
bzoj2179 bzoj3160 bzoj2194
UR#34 多项式乘法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值