CF993E Nikita and Order Statistics

Solution

考虑先将序列 (<a_i>) 转换,定义序列 (<b_i>)。令 (b_i=[a_i<X]),那么对 (b_i) 做前缀和的话,得到的 (s_i) 就表示前 (i) 个数中小于 (X) 的数的个数。那么对于一个特定的 (k) ,我们就需要回答 (s_i=s_j+k) 的有序对 ((i,j)) 的个数。我们成功地将区间计数转换成了元素统计,这实际上是这类问题的惯用伎俩。

再进一步,(s) 相同的前缀可以合并,令 (c_i) 表示 (s=i) 的前缀个数。那么对于答案 (G(k)) ,就有

[G(k)=sum_{i=0}^{m-k} c_i imes c_{i+k} ]

其中 (m) 为所有小于 (X) 的数的个数,容易知道当 (i>m-k)(c_{i+k}) 都为零,所以不用统计。

到了这一步就很简单了,容易发现是一个差卷积的形式。具体地,令 (A_{i}=c_i)(B_{i}=c_{m-i}),那么上式就可以写成

[G(k)=sum_{i=0}^{m-k} A_{i} imes B_{m-i-k} ]

求出 (A)(B) 的卷积后再把前 (m) 项 reverse 一下,就能得到 (G) 序列。复杂度 (O(nlog n))

特别的,(G(0)) 并不能这么处理,我们可以单独算出 (G(0)),复杂度 (O(n))

[G(0)=sum_{i=0}^m frac{c_i(c_i-1)}{2} ]

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define N ((1<<21)+3)
#define db double
#define ll long long

const db PI=acos(-1);

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

struct Complex{
    db x,y;
    Complex(db x_=0,db y_=0):x(x_),y(y_){}
    Complex operator +(const Complex X){return Complex(x+X.x,y+X.y);}
    Complex operator -(const Complex X){return Complex(x-X.x,y-X.y);}
    Complex operator *(const Complex X){return Complex(x*X.x-y*X.y,x*X.y+y*X.x);}
}a[N];

int n,X,m=0,op=0,rk[N];
ll G[N];

void FFT(){
    for(int i=0;i<n;i++)
        if(i<rk[i]) swap(a[i],a[rk[i]]);
    for(int p=2;p<=n;p<<=1){
        int len=p>>1;
        Complex w(cos(2*PI/p),op*sin(2*PI/p));
        for(int k=0;k<n;k+=p){
            Complex now(1.0,0.0);
            for(int l=k;l<k+len;l++){
                Complex t=now*a[l+len];
                a[l+len]=a[l]-t;
                a[l]=a[l]+t;
                now=now*w;
            }
        }
    }
}

int main(){
    n=read(),X=read();
    a[0]=1; int n_=n;ll ret=0;
    for(int i=1;i<=n;i++){
        int x=read();
        if(x<X) a[++m].x=1;
        else a[m].x++;
    }
    for(int i=0;i<=m;i++) a[m-i].y=a[i].x,ret+=(ll)a[i].x*(a[i].x-1)/2;
    for(n=1;n<=(m<<1);n<<=1);
    for(int i=0;i<n;i++)
        rk[i]=(rk[i>>1]>>1)|((i&1)? n>>1:0);
    op=1,FFT();
    for(int i=0;i<n;i++) a[i]=a[i]*a[i];
    op=-1,FFT();
    for(int i=0;i<=m;i++) G[i]=(ll)(a[i].y/n/2+0.5);
    reverse(G,G+1+m),G[0]=ret;
    for(int i=0;i<=n_;i++) printf("%lld ",G[i]);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/14363493.html