CF993E Nikita and Order Statistics
题意:
给你一个数组 a1∼na_{1 \sim n}a1∼n,对于 k=0∼nk = 0 \sim nk=0∼n,求出有多少个数组上的区间满足:区间内恰好有 k 个数比 x 小。 x 为一个给定的数。
n≤2×105n \le 2 \times 10^5n≤2×105
−1e9<=ai<=1e9-1e9<=a_i<=1e9−1e9<=ai<=1e9
题解:
因为x是常数,也就是说对于每个数只有贡献和无贡献之分
所以我们把所有大于等于X的数都改为0,小于X的数都改为1,这样原问题就转化为问有多少个连续序列满足和为k
用公式表示就是:∑i=1n∑j=1n[ai−aj=k]\sum_{i=1}^n\sum_{j=1}^n[a_i-a_j=k]∑i=1n∑j=1n[ai−aj=k]
设前缀和sumi=∑j=1iajsum_i=\sum_{j=1}^ia_jsumi=∑j=1iaj
再设Si=∑[sumj=i],S0=0S_i=\sum[sum_j=i],S_0=0Si=∑[sumj=i],S0=0,SiS_iSi就表示前缀和等于i的数量
原问题就变成求:∑i=k+1nSi⋅Si−k\sum_{i=k+1}^nS_i⋅S_{i-k}i=k+1∑nSi⋅Si−k
比如k=2时,我们要查询区间和等于2的数量,答案就是区间前缀和等于3的数量乘以区间和等于1的数量,前缀和等于4的数量乘以前缀和等于2的数量…。因为前缀和等于3的减去前缀和等于1得到的这段区间一定是等于2的,因此可以这样转化
这个式子看着愈发熟悉。。好像是卷积,卷起来
∑i=k+1nSi⋅Si−k=∑i−j=kSi⋅Sj=∑i+j=kSi⋅S−j=∑i+j=n+kSi⋅Sn−j\sum_{i=k+1}^nS_i⋅S_{i-k}=\sum_{i-j=k}S_i⋅S_{j}=\sum_{i+j=k}S_i⋅S_{-j}=\sum_{i+j=n+k}S_i⋅S_{n-j}i=k+1∑nSi⋅Si−k=i−j=k∑Si⋅Sj=i+j=k∑Si⋅S−j=i+j=n+k∑Si⋅Sn−j
答案就是n+k的系数,k=0时要特判
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI=acos(-1.0);
struct Complex{double x,y;Complex(double _x=0.0,double _y=0.0){x=_x;y=_y;}Complex operator -(const Complex &b)const{return Complex(x-b.x,y-b.y);}Complex operator +(const Complex &b)const{return Complex(x+b.x,y+b.y);}Complex operator *(const Complex &b)const{return Complex(x*b.x-y*b.y,x*b.y+y*b.x);}
};
void change(Complex y[],int len)
{int i,j,k;for(i=1,j=len/2;i<len-1;i++){if(i<j) swap(y[i],y[j]);k=len/2;while(j>=k){j-=k;k/=2;}if(j<k)j+=k;}
}
void fft(Complex y[],int len,int on)
{change(y,len);for(int h=2;h<=len;h<<=1){Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));for(int j=0;j<len;j+=h){Complex w(1,0);for(int k=j;k<j+h/2;k++){Complex u=y[k];Complex t=w*y[k+h/2];y[k]=u+t;y[k+h/2]=u-t;w=w*wn;}}}if(on==-1)for(int i=0;i<len;i++)y[i].x/=len;
}
const int MAXN=800010;
Complex x1[MAXN],x2[MAXN];
int str1[MAXN/2],str2[MAXN/2],a[MAXN];
ll sum[MAXN];
int _sum[MAXN];
int t,n,x;
int main()
{scanf("%d%d",&n,&x);memset(str1,0,sizeof str1);memset(str2,0,sizeof str2);str1[0]=1;for(int i=1;i<=n;i++){scanf("%d",&a[i]);if(a[i]<x)a[i]=1;else a[i]=0;_sum[i]=_sum[i-1]+a[i];str1[_sum[i]]+=1;}for(int i=0;i<=n;i++){str2[i]=str1[n-i];}int len=1,len1=n+1,len2=n+1;while(len<len1*2||len<len2*2) len<<=1;for(int i=0;i<len1;i++)x1[i]=Complex(str1[i],0);for(int i=len1;i<len;i++)x1[i]=Complex(0,0);for(int i=0;i<len2;i++)x2[i]=Complex(str2[i],0);for(int i=len2;i<len;i++)x2[i]=Complex(0,0);
// for(int i=0;i<len;i++){
// cout<<x2[i].x<<" ";
// }
// cout<<endl;
// cout<<"--"<<endl;
// for(int i=0;i<len;i++){
// cout<<str2[i]<<" ";
// } cout<<endl;fft(x1,len,1);fft(x2,len,1);for(int i=0;i<len;i++)x1[i]=x1[i]*x2[i];
// for(int i=0;i<len;i++)
// cout<<"x1[i]="<<x1[i].x<<endl;
// cout<<endl;fft(x1,len,-1);ll ans=0,num=0;for(int i=1;i<=n+1;i++){if(a[i]==0&&i!=n+1)num++;else {ans+=num*(num+1)/2;num=0; } }cout<<ans<<" ";// cout<<endl;for(int i=1;i<=n;i++){cout<<(ll)(x1[i+n].x+0.5)<<" ";}return 0;
}