• CF993E Nikita and Order Statistics


    IX.CF993E Nikita and Order Statistics

    首先,一上来就能想到的思路,对于(< x)的位置,赋成(1);对于(geq x)的位置,赋成(0),然后跑前缀和,设为(sum_x)。这样子,(x)在某个子串([l,r])中的排名就是(sum_r-sum_{l-1})

    (x)(sum)数组中出现了(cnt_x)次。则某个(k)的答案(ans_k=sum_{i=k}^ncnt_i*cnt_{i-k})。因为挑选任何一个(sum=i)的前缀与任何一个(sum=i-k)的前缀,都可以拼成一个(k)出来。

    遇事不决就翻转。我们设(f)表示原本的(cnt)(g)表示翻转((g_x=cnt_{n-x}))后的(cnt)

    则现在,(ans_{n-k}=sumlimits_{i+j=k}f_i*g_j)。是卷积的形式,直接FFT!!!

    不过有几个注意点:

    1.(sum_0),即空前缀和,也要计入(cnt),不然类似于串([1,r])的就不会被计入。估计只有我一个人才会忘记吧

    2.(ans_0)要特判,因为某些串你瞎卷卷可能卷出来长度为(0)的串。可以直接(O(n))暴力扫一遍即可。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const double pi=acos(-1);
    int n,m,cnt[1<<20],lg,lim=1,h[1<<20],rev[1<<20],sum[1<<20];
    struct cp{
    	double x,y;
    	cp(double u=0,double v=0){x=u,y=v;}
    	friend cp operator +(const cp &u,const cp &v){return cp(u.x+v.x,u.y+v.y);}
    	friend cp operator -(const cp &u,const cp &v){return cp(u.x-v.x,u.y-v.y);}
    	friend cp operator *(const cp &u,const cp &v){return cp(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);}
    }f[1<<20],g[1<<20];
    void FFT(cp *a,int tp){
    	for(int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    	for(int md=1;md<lim;md<<=1){
    		cp rt=cp(cos(pi/md),tp*sin(pi/md));
    		for(int stp=md<<1,pos=0;pos<lim;pos+=stp){
    			cp w=cp(1,0);
    			for(int i=0;i<md;i++,w=w*rt){
    				cp x=a[pos+i],y=w*a[pos+md+i];
    				a[pos+i]=x+y;
    				a[pos+md+i]=x-y;
    			}
    		}
    	}
    }
    int zero(){
    	int pos=0,res=0;
    	for(int i=1;i<=n;i++){
    		if(sum[i]!=sum[i-1])pos=i;
    		res+=i-pos;
    	}
    	return res;
    }
    signed main(){
    	scanf("%lld%lld",&n,&m);
    	while(lim<=n*2+1)lim<<=1,lg++;
    	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    	cnt[0]++;
    	for(int i=1,x;i<=n;i++)scanf("%lld",&x),sum[i]=sum[i-1]+(x<m),cnt[sum[i]]++;
    	for(int i=0;i<=n;i++)f[i]=cp(cnt[i],0),g[i]=cp(cnt[n-i],0);
    	FFT(f,1),FFT(g,1);
    	for(int i=0;i<lim;i++)f[i]=f[i]*g[i];
    	FFT(f,-1);
    	for(int i=0;i<=n;i++)h[n-i]=(int)(f[i].x/lim+0.5);
    	printf("%lld ",zero());
    	for(int i=1;i<=n;i++)printf("%lld ",h[i]);
    	return 0;
    }
    
  • 相关阅读:
    人类历史上最智慧的169条警世箴言(句句珠玑,发人深省)
    最负责任的回答
    成大事必须依靠的五种人
    一生的伤痕
    有谁愿意陪我一程
    惜缘
    那朵美丽的格桑花,你是否依然绽放?
    今生今世只等你
    成就一生的15条黄金法则
    遇到困难挫折也不要悲观:每个人生来就是冠军(转)
  • 原文地址:https://www.cnblogs.com/Troverld/p/12772263.html
Copyright © 2020-2023  润新知