zoukankan      html  css  js  c++  java
  • CF993E Nikita and Order Statistics 多项式卷积 快速傅里叶变换

    题意:

      给你一个数组a1~an,对于k=0~n,求出有多少个数组上的区间满足:区间内恰好有k个数比x小。x为一个给定的数。n<=10^5。值域没有意义。

     

    分析:

      大神们都说这道题是一个套路题,真是长见识%%%。

      首先我们可以将题面转化,因为x是预先给出的,所以我们可以对其进行预处理,将数列中小于x的数都设为1,其他都为0,然后求一个前缀和,另前缀和数组为s[i]我们开一个数组v[i],记录在前缀和数组中数值i出现的次数。

      然后我们可以得到这样一个式子

      (据说看到这个式子就是套路了)

      然后我们对这个式子进行一个转化。

      转化:

      之后,我们就可以修改上面的式子,变成这样,

      有些经验的选手可以看得出,这个形式就是一个卷积的形式。

      所以我们就直接把v数组和t数组看成多项式,用fft做一遍卷积,之后n+k次项的系数就是ans_k

      k=0时需要特殊处理一下,要去除空串的影响,并且当k=0是,由于i和j的顺序问题,所以每种情况都统计了两次,最后要除以2。

    代码:

     1 #include<bits/stdc++.h>
     2 #include<complex>
     3 #define db double
     4 #define ll long long
     5 #define cp complex<db>
     6 using namespace std;
     7 const int N=1000005;
     8 const db pi=acos(-1);
     9 int m,l,r[N],cnt[N],s[N],x;
    10 cp a[N],b[N],omg[N],inv[N];ll n,ans[N];
    11 void init(){
    12     for(int i=0;i<n;i++)
    13     omg[i]=cp(cos(2*pi*i/n),sin(2*pi*i/n)),
    14     inv[i]=conj(omg[i]);
    15 } void fft(cp *a,cp *tmp){
    16     int lm=0;while((1<<lm)<n) lm++;
    17     for(int i=0;i<n;i++){int t=0;
    18         for(int j=0;j<lm;j++)
    19         if((i>>j)&1) t|=(1<<(lm-j-1));
    20         if(i<t) swap(a[i],a[t]); 
    21     } for(int l=2;l<=n;l*=2){
    22         int m=l/2;
    23         for(cp *p=a;p!=a+n;p+=l)
    24         for(int i=0;i<m;i++){
    25             cp t=tmp[n/l*i]*p[i+m];
    26             p[i+m]=p[i]-t;p[i]+=t;
    27         }
    28     } return ;
    29 } int main(){
    30     scanf("%lld%d",&n,&x);cnt[0]=1;
    31     for(int i=1,y;i<=n;i++)
    32     scanf("%d",&y),s[i]=s[i-1]+(y<x),cnt[s[i]]++;
    33     for(int i=0;i<=n;i++) a[i]=b[n-i]=cnt[i];
    34     int q=n;n=1;while(n<=(q<<1)) n<<=1;
    35     init();fft(a,omg);fft(b,omg);
    36     for(int i=0;i<n;i++) a[i]*=b[i];
    37     fft(a,inv);
    38     ans[0]=(ll)((a[q].real()/n+0.5)-1ll*q-1)>>1ll;
    39     printf("%lld",ans[0]);
    40     for(int i=1;i<=q;i++)
    41     ans[i]=(ll)floor(a[q+i].real()/n+0.5),
    42     printf(" %lld",ans[i]);puts("");return 0;
    43 }
    fft 快速傅里叶变换
  • 相关阅读:
    大道至简第五章读后感
    课后作业1:字串加密
    String类中的equals()方法:
    构架之美读后感5
    构架之美读后感4
    构架之美读后感3
    构架之美读后感2
    构架之美读后感1
    关于联想y485p装win10显卡驱动问题
    软件需求与分析课堂讨论一
  • 原文地址:https://www.cnblogs.com/Alan-Luo/p/10409145.html
Copyright © 2011-2022 走看看