fft/ntt

15 篇文章 0 订阅
1 篇文章 0 订阅

cf827E
cf773E
cf662C
cf632E
cf623E
cf553E
cf528D
cf472G
cf438E
cf300D
cf286E

bzoj3160
题意:在一个仅仅含有a,b的字符串里选取一个子序列,使得:
1.位置和字符都关于某条对称轴对称;
2.不能是连续的一段。
分析:
这里写图片描述
对于每一条对称轴只要求在对称轴两边的对应位置上有相同字符的位置有多少个。
答案就是 2len1len (减去空串和连续的串)
但这东东它可以是不连续的…..
我们可以将
a b a a b a
1 0 1 1 0 1
发现:多项式
1 0 1 1 0 1
*1 0 1 1 0 1
假如答案某一位上是1,则 xk 前的系数恰好为以第k个位置(包括间隙,从0开始)为对称轴而对称的a的个数。
fft搞两波问题就解决啦~
bzoj3456
bzoj4555
bzoj3992
bzoj4503
bzoj3625
bzoj4451
bzoj3771
bzoj2179模板
bzoj3527
bzoj2194
bzoj3557
bzoj4259
xsy2182
xsy1903
xsy2106
xsy2166
xsy2179
题意:
这里写图片描述

分析:
考虑由大往小枚举最大值,对于某一最大值为 M 的区间 [l,r] ,满足 a[pi]=M 的元素将区间切割为若干子区间,那么这些子区间对长度的积对答案的某一项有等值的贡献,暴力枚举需要 O(n2) 的时间,整体考虑那些对答案某一项有贡献的子区间对的积,它们满足卷积的性质。因此只需将长度序列与其反序列作类似多项式乘法的fft,即可将时间优化为 O(nlogn) 。子区间递归处理即可。

//核心代码
void gao(int x){
    h[top=0]=x-l[x]+1;v[x]=1;
    while (1){
        h[++top]=r[x]-x+1;
        if (d[r[x]+1]!=d[x]) break;
        x=r[x]+1;v[x]=1;
    }
    for (L=-1,len=1;len<=top*2;len*=2) L++;
    for (int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<L);
    for (int i=0;i<=top;i++) a[i]=b[top-i]=point(h[i],0);
    for (int i=top+1;i<len;i++) a[i]=b[i]=point(0,0);
    fft(a,len,1);fft(b,len,1);
    for (int i=0;i<len;i++) a[i]=a[i]*b[i];
    fft(a,len,-1);
    for (int i=1;i<=top;i++) s[i]+=(ll)a[top-i].x;
}

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",d+i);
    top=0;q[top].id=0;
    for (int i=1;i<=n;i++){
        while (top && q[top].x<d[i]) top--;
        l[i]=q[top].id+1;q[++top]=node(d[i],i);
    }
    top=0;q[top].id=n+1;
    for (int i=n;i;i--){
        while (top && q[top].x<d[i]) top--;
        r[i]=q[top].id-1;q[++top]=node(d[i],i);
    }

    for (int i=1;i<=n;i++)
    if (!v[i]) gao(i);
    for (int i=1;i<=n;i++)
        ans+=s[i]^i;
    printf("%lld\n",ans);
}

12.fft

struct cp
{
    double x,y;
    inline cp(double _x=0.0,double _y=0.0)
    {
        x=_x;
        y=_y;
    }
    friend inline cp operator + (cp a,cp b)
    {
        return cp(a.x+b.x,a.y+b.y);
    }
    friend inline cp operator - (cp a,cp b)
    {
        return cp(a.x-b.x,a.y-b.y);
    }
    friend inline cp operator * (cp a,cp b)
    {
        return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
    }
}a[N],b[N],c[N],t[N];

void fft(cp *a,int len,int kd)
{
    //kd=1时将系数表达式转换为点值表达式(就是n个点描述一个多项式)
    //kd=-1时将点值表达式转换成系数表达式
    for (int i=0;i<len;i++) t[i]=a[rev[i]];
    for (int i=0;i<len;i++) a[i]=t[i];
    //低级写法,ntt的写法更优高级

    for (int i=2;i<=len;i*=2)
    {
        cp wn(cos(2*pi/i),kd*sin(2*pi/i));//公比
        for (int k=0;k<len;k+=i)
        {
            cp w(1,0);//螺旋因子
            for (int j=0;j<i/2;j++)
            {
                cp x=a[k+j];//蝶形操作
                cp y=a[k+j+i/2]*w;
                a[k+j]=x+y;//前半部分
                a[k+j+i/2]=x-y;//后半部分
                w=w*wn;
            }
        }
    }
    if(kd==-1)///IDFT  
    for(int i=0;i<len;i++)  
        a[i].x=round(a[i].x/len);//4舍5入  
}

//rev[]的预处理
    for(m=n,n=1;n<=m<<1;n<<=1) l++;
    for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);

13.ntt

void ntt(ll *a,int len,int f) {
    for (int i=0;i<len;i++)
        if (rev[i]>i) swap(a[i],a[rev[i]]);
    for(int i=2;i<=len;i<<=1) {
        ll wn=power(3,((mod-1)/i*f+mod-1)%(mod-1)),m=i>>1;
        //3为mod的原根
        for(int j=0;j<len;j+=i) {
            ll w=1;
            for(int k=0;k<m;k++,w=w*wn%mod) {
                ll x=a[j+k],y=a[j+k+m]*w%mod;
                a[j+k]=(x+y)%mod,a[j+k+m]=(x-y+mod)%mod;
            }
        }
    }
    if(f==-1) {
        ll ni=power(len,mod-2);
        for(int i=0;i<len;i++) a[i]=a[i]*ni%mod;
    }
}

题型:

1.对应为相乘—->将其中一个反转

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值