FFT/NTT部分题表

3 篇文章 0 订阅

前言:

因为懒,所以 写成题表模式。

bzoj 4836

假如只有一个操作,那么直接做卷积就好了,减法可以将下面的哪个数组翻转过来。
现在要求只能与大的数卷,那么就分治+fft就好了。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#define LL long long
using namespace std;
const double pi=acos(-1);
int m1,m2;
int a[50010],b[50010];
LL c[100010];
int bin[200010];
struct P{
    double x,y;
    P() {x=y=0;}
    P(double a,double b) {x=a;y=b;}
}A[200010],B[200010];
P operator+(P x,P y){return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y){return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y){return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
inline int read(int &x)
{
    int ans=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
    x=x>ans?x:ans;return ans*f;
}
void fft(P *a,int n,int op)
{
    for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
    for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
    for(int i=1;i<n;i<<=1)
    {
        P wn=P(cos(pi/i),op*sin(pi/i)),t,w;
        for(int j=0;j<n;j+=i<<1)
        {
            w=P(1,0);
            for(int k=0;k<i;k++)
            {
                t=a[i+j+k]*w;w=w*wn;
                a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
            }
        }
    }
}
void mul(P *A,P *B,int n)
{
    fft(A,n,1);fft(B,n,1);
    for(int i=0;i<n;i++) A[i]=A[i]*B[i];fft(A,n,-1);
}
void solve(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)/2;
    int n=1;while(n<=(r-l+1)) n<<=1;
    for(int i=0;i<n;i++) A[i]=P(0,0),B[i]=P(0,0);
    for(int i=0;i<mid-l+1;i++) A[i].x=a[l+i];
    for(int i=0;i<r-mid;i++) B[i].x=b[mid+i+1];
    mul(A,B,n);for(int i=0;i<n;i++) c[i+l+mid+1]+=(LL)(A[i].x/n+0.5);
    for(int i=0;i<n;i++) A[i]=P(0,0),B[i]=P(0,0);
    for(int i=0;i<r-mid;i++) A[i].x=a[mid+i+1];
    for(int i=0;i<mid-l+1;i++) B[i].x=b[mid-i];
    mul(A,B,n);for(int i=0;i<n;i++) c[i+1]+=(LL)(A[i].x/n+0.5);
    solve(l,mid);solve(mid+1,r);
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,m,q;n=read(m1);m=read(m1);q=read(m1);
        memset(a,0,sizeof(a));m1=m2=0;
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++) a[read(m1)]++;
        for(int j=1;j<=m;j++) b[read(m2)]++;
        solve(0,max(m1,m2));
        for(int i=0;i<=m1;i++) c[0]+=(LL)a[i]*(LL)b[i];
        while(q--) printf("%lld\n",c[read(m1)]);
    }
}

bzoj 3509

直接做不好做,考虑分块。
块内的直接 n2 n 2 dp,对于三个数都在不同块的情况就枚举中间数在哪个块,然后fft。
块的大小用1800比较优。
code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cstdlib>
#define LL long long
using namespace std;
int n,a[100010];
const double pi=acos(-1);
struct P{
    double x,y;
    P() {x=y=0;}
    P(double a,double b) {x=a;y=b;}
}A[70010],B[70010];
int inf=30000;
int g1[70010],g2[70010];
int pre[100010],len=1800,L[70],R[70];
int bin[70010];
LL ans=0;
P operator+(P x,P y){return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y){return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y){return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
inline int read()
{
    int ans=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
    return ans*f;
}
void fft(P *a,int n,int op)
{
    for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
    for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
    for(int i=1;i<n;i<<=1)
    {
        P wn=P(cos(pi/i),op*sin(pi/i)),t,w;
        for(int j=0;j<n;j+=i<<1)
        {
            w=P(1,0);
            for(int k=0;k<i;k++)
            {
                t=a[i+j+k]*w;w=w*wn;
                a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
            }
        }
    }
    if(op==-1) for(int i=0;i<n;i++) a[i].x/=n;
}
void solve(int l,int r)
{
    for(int i=l;i<=r;i++) g1[a[i]]--;
    for(int i=0;i<(1<<16);i++) A[i]=P(g1[i],0),B[i]=P(g2[i],0);
    fft(A,1<<16,1);fft(B,1<<16,1);
    for(int i=0;i<(1<<16);i++) A[i]=A[i]*B[i];
    fft(A,1<<16,-1);
    for(int i=l;i<=r;i++) ans+=(LL)(A[a[i]<<1].x+0.5);
    for(int i=l;i<=r;i++)
    {
        for(int j=i+1;j<=r;j++)
        {
            if(2*a[j]-a[i]>=0&&2*a[j]-a[i]<=inf) ans+=(LL)g1[2*a[j]-a[i]];
            if(2*a[i]-a[j]>=0&&2*a[i]-a[j]<=inf) ans+=(LL)g2[2*a[i]-a[j]];
        }
        g2[a[i]]++;
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),g1[a[i]]++;
    for(int i=1;i<=n;i++)
    {
        pre[i]=i/len+1;
        if(pre[i]!=pre[i-1]) L[pre[i]]=i,R[pre[i-1]]=i-1;
    }
    R[pre[n]]=n;
    for(int i=1;i<=pre[n];i++) solve(L[i],R[i]);
    printf("%lld\n",ans);
}

bzoj 2194

裸题不解释。

3771

大力容斥即可。

bzoj 4827

化下式子可以发现,c与ans是个二次函数关系,那么可以算出卷积后枚举c得到最优解。
于是将上面的式子倍长,下面的式子翻转,做fft即可。

bzoj 4259/bzoj 4503

好题,首先有一个暴力点的做法,首先将所有a标为1,其它为0,做卷积 ,然后将所有b标为1,其它为0,在做卷积……将答案累加起来,看一下某一位的值是否是匹配串的长度即可。
按这样稳稳的T掉,于是定义两个字符的差异为: s[i]t[i](s[i]t[i])2 s [ i ] ∗ t [ i ] ∗ ( s [ i ] − t [ i ] ) 2 ,?看作0。
显然只有当两个字符一样时,或者有?时,才会是0,而且这个式子是可以卷积的。
code(4259):

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const double pi=acos(-1),eps=1e-8;
struct P{
    double x,y;
    P() {x=y=0;}
    P(double a,double b) {x=a;y=b;}
}a[1200000],b[1200000],c[1200000],ans[1200000];
P operator+(P x,P y) {return P(x.x+y.x,x.y+y.y);}
P operator-(P x,P y) {return P(x.x-y.x,x.y-y.y);}
P operator*(P x,P y) {return P(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
int bin[1200000],len1,len2;
char S[300000],T[300000];
int s[300000],t[300000];
void fft(P *a,int n,int op)
{
    for(int i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
    for(int i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
    for(int i=1;i<n;i<<=1)
    {
        P wn=P(cos(pi/i),sin(pi/i)*op),t,w;
        for(int j=0;j<n;j+=i<<1)
        {
            w=P(1,0);
            for(int k=0;k<i;k++)
            {
                t=a[j+i+k]*w;w=w*wn;
                a[i+j+k]=a[j+k]-t;a[j+k]=a[j+k]+t;
            }
        }
    }
    if(op==-1) for(int i=0;i<n;i++) a[i].x/=n;
}
int n=1;
int num=0,list[100010];
void print(P *a,int n){for(int i=0;i<n;i++) printf("%0.lf ",a[i].x);printf("\n");}
int main()
{
    scanf("%d %d",&len2,&len1);
    scanf("%s %s",T+1,S+1);
    //len1=strlen(S+1);len2=strlen(T+1);
    for(int i=1;i<=len1;i++) s[i]=S[i]=='*'?0:S[i]-'a'+1;
    for(int i=1;i<=len2;i++) t[i]=T[i]=='*'?0:T[i]-'a'+1;
    while(n<=len1+len2) n<<=1;
    for(int i=1;i<=len2;i++) a[len2-i+1].x=t[i]*t[i]*t[i];
    for(int i=1;i<=len1;i++) b[i].x=s[i];
    //print(a,n);print(b,n);
    fft(a,n,1);fft(b,n,1);
    for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
    for(int i=0;i<n;i++) a[i].x=a[i].y=b[i].x=b[i].y=0;
    for(int i=1;i<=len2;i++) a[len2-i+1].x=-2*t[i]*t[i];
    for(int i=1;i<=len1;i++) b[i].x=s[i]*s[i];
    fft(a,n,1);fft(b,n,1);
    for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
    for(int i=0;i<n;i++) a[i].x=a[i].y=b[i].x=b[i].y=0;
    for(int i=1;i<=len2;i++) a[len2-i+1].x=t[i];
    for(int i=1;i<=len1;i++) b[i].x=s[i]*s[i]*s[i];
    fft(a,n,1);fft(b,n,1);
    for(int i=0;i<n;i++) ans[i]=ans[i]+a[i]*b[i];
    fft(ans,n,-1);
    for(int i=1;i<=len1-len2+1;i++) if(ans[i+len2].x<0.5) list[++num]=i;
    printf("%d\n",num);
    for(int i=1;i<=num;i++) printf("%d ",list[i]);
}

bzoj 4332

显然可以写出一个转移多项式g。
那这题相当于求 f(g+g2+g3+g4++gn) f ( g + g 2 + g 3 + g 4 + … … + g n ) 分治即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值