HDU-5730(CDQ+FFT/NTT)

HDU-5730(CDQ+FFT/NTT)

题意:将长度为\(n\)的序列分成若干段,每段\([l,r]\)的权值为\(a_{r-l+1}\),一种分法的权值为所有段的乘积,求所有可能的分法的权值和

根据题意可以得到简单\(dp\)

\(dp_0=1,dp_i=\sum_0^{i-1}dp_j \cdot a_{i-j}\)

可以看到是一个\(i-j\)形式的作差卷积

但是直接卷积我们无法保证先求出了\(dp_j\),所有可以用\(CDQ\)分治优化,复杂度\(n\log^2 n\)

具体实现见代码

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); } 

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const double PI=acos(-1);

const int N=(1<<18)+4,P=313;

int n;
int a[N],dp[N];
struct Cp{
    double x,y;
    Cp operator + (const Cp t) const { return (Cp){x+t.x,y+t.y}; }
    Cp operator - (const Cp t) const { return (Cp){x-t.x,y-t.y}; }
    Cp operator * (const Cp t) const { return (Cp){x*t.x-y*t.y,x*t.y+y*t.x}; }
} A[N],B[N];

Cp w[N];
int rev[N];
void FFT(int n,Cp *a,Cp *w){
    rep(i,1,n-1) if(rev[i]>i) swap(a[i],a[rev[i]]);
    for(reg int i=1;i<n;i<<=1) {
        int len=i*2;
        for(reg int l=0;l<n;l+=len) {
            for(reg int j=l;j<l+i;++j) {
                Cp t=a[j+i]*w[n/len*(j-l)];
                a[j+i]=a[j]-t;
                a[j]=a[j]+t;
            }
        }
    }
}

void Solve(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1;
    Solve(l,mid); //先求出左边所有的

    int R=1,c=-1;
    while(R<=r-l+1) R<<=1,c++;
    rep(i,1,R) rev[i]=(rev[i>>1]>>1)|((i&1)<<c);
    w[0]=(Cp){1,0};
    w[1]=(Cp){cos(2*PI/R),sin(2*PI/R)};
    rep(i,2,R) w[i]=w[i-1]*w[1];
    rep(i,l,mid) A[i-l]=(Cp){1.0*dp[i],0};
    rep(i,1,r-l+1) B[i]=(Cp){1.0*a[i],0};
    FFT(R,A,w),FFT(R,B,w);
    rep(i,0,R) w[i].y=-w[i].y,A[i]=A[i]*B[i];
    FFT(R,A,w);
    rep(i,mid+1,r) dp[i]=(dp[i]+ll(A[i-l].x/R+0.5))%P;
    rep(i,0,R) A[i].x=A[i].y=B[i].x=B[i].y=0;
    //让左边已经求出来的向右边转移

    Solve(mid+1,r);
    //让右边之间转移
}

int main(){
    while(~scanf("%d",&n) && n) {
        rep(i,1,n) a[i]=rd()%P,dp[i]=0;
        dp[0]=1;
        Solve(0,n);
        printf("%d\n",dp[n]);
    }
}

原文地址:https://www.cnblogs.com/chasedeath/p/12101353.html

时间: 2024-10-11 17:29:55

HDU-5730(CDQ+FFT/NTT)的相关文章

HDU 5730 - Shell Necklace

题意: 给出连续的1-n个珠子的涂色方法 a[i](1<=i<=n), 问长度为n的珠链共有多少种涂色方案 分析: 可以得到DP方程: DP[n] = ∑(i=1,n) (DP[n-i]*a[i]). 该方程为卷积形式,故 CDQ + FFT CDQ: 将 [l,r] 二分, 先得到[l,mid]的答案,再更新[l,mid]对[mid+1,r]的贡献.   对任意 DP[j](mid+1 <= j <= r), [l,mid] 对其贡献为 ∑(i=l,mid) (DP[i]*a[j

Hdu 1402 (FFT)

题目链接 A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 12490    Accepted Submission(s): 2206 Problem Description Calculate A * B. Input Each line will contain two integers A and

FFT/NTT做题方法与调试技巧(+提高码题效率的一些想法)

(其实本文应该写成了"结合FFT讨论的调试技巧+码题方法",语文不好一写文章就偏题QAQ) 有意见欢迎提出,有遗漏欢迎补充! FFT(快速傅里叶变换)/NTT(数论变换)是卷积运算常见而实用的优化 但是FFT/NTT的处理过程并不像暴力运算(差不多是多项式乘法)那样能够直观地反映卷积结果的实时变化. 因此在使用FFT时将会或多或少地加大调试的难度. 如果调试程序时直接跟踪变量,每步手算结果比对,不仅会耽误大量时间,而且效果可能并不理想. 直接肉眼查错效率可能也不太高. 但也正由于FFT

FFT NTT 错误总结(持续更新)

FFT NTT错误总结 1 处理\(r\)数组时忘记赋值 r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1)); 2 负数重载运算符 point operator * (point a,point b){ return point(a.x * b.x - a.y * b.y,a.x * b.y + a.y * b.x); } 3 欧拉公式记不清楚 point Wn = point(cos(Pi / mid),type *

FFT/NTT基础题总结

在学各种数各种反演之前把以前做的$FFT$/$NTT$的题整理一遍 还请数论$dalao$口下留情 T1快速傅立叶之二 题目中要求求出 $c_k=\sum\limits_{i=k}^{n-1}a_i*b_{i-k}$ 首先可以把$a$翻转, $c_k=\sum\limits_{i=k}^{n-1}a_{n-1-i}*b_{i-k}$ $c_k=\sum\limits_{i=0}^{n-k-1}a_{n-k-1-i}*b_{i}$ 也就是说对新的$a$,$b$数组做一遍$FFT$得到的便是$c$数

多项式$fft$,$ntt$,$fwt$初步

最近在学多项式和生成函数. 上课听$lnc$大神讲还是$mengbier$. 作为多项式的前置芝士,$fft,ntt$等是必学的. 在此记录一些关于$fft,ntt,fwt$的知识及例题... FFT: 应用在处理$\sum _{i+j=k} f[i]*g[j]$的卷积上. 看网上大佬的博客,基本入了门吧. 自己的关于原理的一些见解: 多项式有系数表示和点值表示,两种表示方法可以相互转化. FFT可以在$O(n*longn)$内解决多项式乘法. 具体是,点值表示的方法应用在多项式上是$O(n)$

HDU-4609(FFT/NTT)

HDU-4609(FFT/NTT) 题意: 给出n个木棒,现从中不重复地选出3根来,求能拼出三角形的概率. 计算合法概率容易出现重复,所以建议计算不合法方案数 枚举选出的最大边是哪条,然后考虑剩下两条边之和小于等于它 两条边之和为\(x\)的方案数可以\(FFT/NTT\)得到,是一个简单的构造 即\(f(x)=\sum x^{length_i}\),求出\(f(x)^2\),就能得到和的方案数,但是会重复,包括自己和自己算,一对算两次 处理一下前缀和即可 #include<bits/stdc+

HDU 5730 Shell Necklace(CDQ分治+FFT)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5730 [题目大意] 给出一个数组w,表示不同长度的字段的权值,比如w[3]=5表示如果字段长度为3,则其权值为5,现在有长度为n的字段,求通过不同拆分得到的字段权值乘积和. [题解] 记DP[i]表示长度为i时候的答案,DP[i]=sum_{j=0}^{i-1}DP[j]w[i-j],发现是一个卷积的式子,因此运算过程可以用FFT优化,但是由于在计算过程中DP[j]是未知值,顺次计算复杂度是O(

hdu 5730 Shell Necklace fft+cdq分治

题目链接 dp[n] = sigma(a[i]*dp[n-i]), 给出a1.....an, 求dp[n]. n为1e5. 这个式子的形式显然是一个卷积, 所以可以用fft来优化一下, 但是这样也是会超时的. 所以可以用cdq分治来优化. cdq分治就是处理(l, mid)的时候, 将dp[l]...dp[mid]对dp[mid+1]...dp[r]做的贡献都算出来. #include <bits/stdc++.h> using namespace std; #define pb(x) pus