CF993E

题意: 

  给你一个数组 $a_{1 \sim n}$,对于 $k = 0 \sim n$,求出有多少个数组上的区间满足:区间内恰好有 $k$ 个数比 $x$ 小。$x$ 为一个给定的数。
  $n \le 2 \times 10^5$。值域没有意义。

分析:

  对于$a_{i}$,若$a_{i}<x$则$a_{i}=1$,反之$a_{i}=0$。

  设$s$为$a$的前缀和,$c$数组为统计的数组,即求:  

$$
\begin{align}
&\sum_{i=k}^{n}s_{i}s_{i-k}\\
&=\sum_{i=k}^{n}s_{i}g_{n-i+k}\\
&=\sum_{i+j=n+k}s_{i}g_{j}
\end{align}
$$

  设$g_{j}=c_{n-i}$,再求卷积即可

  关于反转技巧,可以看这

  关于$ans_{0}$,从实际意义上考虑,是$0$串的个数,即所有$s_{i}=s_{j}$(之类的)的情况

  不想再打式子了,此时会有$i=j$的情况,共$n+1$组,其余由于相等会不分顺序算$2$次,所以要先减再除以$2$。

  本题答案很大,不可以$ntt$,只可用$fft$

code:

  

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1<<22;
const double pi=acos(-1);
struct comp{
    double x,y;
    comp(double xx=0,double yy=0){
        x=xx,y=yy;
    }
};
comp operator +(comp a,comp b){
    return comp(a.x+b.x,a.y+b.y);
}
comp operator -(comp a,comp b){
    return comp(a.x-b.x,a.y-b.y);
}
comp operator *(comp a,comp b){
    return comp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
int r[N],lim,L,n,m,a[N];
comp c[N],d[N];
void fft(comp *f,int fg){
    for(int i=0;i<lim;i++){
        if(i<r[i]){
            swap(f[i],f[r[i]]);
        }
    }
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1;
        comp buf(cos(pi/len),fg*sin(pi/len));
        for(int j=0;j<lim;j+=p){
            comp t(1,0);
            for(int k=j;k<j+len;k++){
                comp w=t*f[k+len];
                f[k+len]=f[k]-w;
                f[k]=f[k]+w;
                t=t*buf;
            }
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        if(x<m) a[i]=1;
        a[i]+=a[i-1];
    }
    for(int i=0;i<=n;i++){
        c[a[i]].x++;
        d[n-a[i]]=c[a[i]];
    }
    while(1<<L<=2*n)L+=1;
    lim=1<<L;
    for(int i=0;i<lim;i++){
        r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
    }
    fft(c,1);
    fft(d,1);
    for(int i=0;i<lim;i++){
        c[i]=c[i]*d[i];
    }
    fft(c,-1);
    for(int i=0;i<=n;i++){
        if(i==0){
            printf("%.0lf ",(c[i+n].x/lim-n-1)/2+0.3);
        }
        else printf("%.0lf ",(c[i+n].x)/lim+0.3);
    }
    puts("");
    return 0;
}

 

  

posted @ 2019-07-25 22:49  天才美少女雪乃  阅读(307)  评论(0编辑  收藏  举报