zoukankan      html  css  js  c++  java
  • [BZOJ 3509] [CodeChef] COUNTARI (FFT+分块)

    [BZOJ 3509] [CodeChef] COUNTARI (FFT+分块)

    题面

    给出一个长度为n的数组,问有多少三元组((i,j,k))满足(i<j<k,a_j-a_i=a_k-a_j)

    (n leq 10^5, a_i leq 30000)

    分析

    (m=max(a_i))

    先做一点变形(a_i+a_k=2a_j).那么枚举j,把j左边的和右边的所有数字找出来,找出有哪些数字对的和为(2a_j)

    维护当前块左边每个数的出现次数cntl,,和右边块的出现次数cntr。

    那么数字和为(2a_j)的方案数就是(sum_{i=0}^{2a_j} cntl(i)cntr(2a_j-i))

    这是一个卷积的形式,可以FFT.令:

    $A(x)=sum_{i=0}^{m} cntl(i) x^i $

    $B(x)=sum_{i=0}^{m} cntr(i) x^i $

    (A*B)的第(2a_j)项的系数就是答案.但是做n次FFT的复杂度是(O(n^2 log n)),显然是跑不过去的.

    我们要平衡一下复杂度,容易想到分块。设块大小为T

    i,j,k均不在同一个块的情况

    我们枚举j在第几个块,从左到右扫描第2~n-1个块,每块把左边和右边FFT卷积。枚举块内的每个数x,和为(2x)的方案数就是卷积结果里第(2x)项的系数.

    时间复杂度(O(frac{n}{B} imes m log m))

    i,j,k至少有2个在同一个块的情况

    直接暴力从小到大在块内枚举(k,j(j<k)).然后讨论j在k前还是在k后。同样维护2个数组cntl表示当前块前面的数的出现次数,cntr表示k后面的数的个数。

    如果i在j前,那么答案就是(cntl[a_j*2-a_k])

    如果i在k后,那么答案就是(cntr[a_k*2-a_j])

    注意到cntl不包含当前块,而cntr包含当前块,这样(i,j,k)在同一个块的情况只会被算1次

    时间复杂度(O(frac{n}{B} imes B imes B)=O(nB))

    总时间复杂度(O(nB+frac{n}{B}mlog m))

    根据基本不等式,(B)(sqrt{m log m})时最优.总时间复杂(O(nsqrt{m log m}))

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define maxn 200000
    using namespace std;
    typedef long double db;
    typedef long long ll;
    const db pi=acos(-1.0);
    struct com{
        double real;
        double imag;
        com(){
    
        } 
        com(double _real,double _imag){
            real=_real;
            imag=_imag;
        }
        com(double x){
            real=x;
            imag=0;
        }
        void operator = (const com x){
            this->real=x.real;
            this->imag=x.imag;
        }
        void operator = (const double x){
            this->real=x;
            this->imag=0;
        }
        friend com operator + (com p,com q){
            return com(p.real+q.real,p.imag+q.imag);
        }
        friend com operator + (com p,double q){
            return com(p.real+q,p.imag);
        }
        void operator += (com q){
            *this=*this+q;
        }
        void operator += (double q){
            *this=*this+q;
        }
        friend com operator - (com p,com q){
            return com(p.real-q.real,p.imag-q.imag);
        }
        friend com operator - (com p,double q){
            return com(p.real-q,p.imag);
        }
        void operator -= (com q){
            *this=*this-q;
        }
        void operator -= (double q){
            *this=*this-q;
        }
        friend com operator * (com p,com q){
            return com(p.real*q.real-p.imag*q.imag,p.real*q.imag+p.imag*q.real);
        }
        friend com operator * (com p,double q){
            return com(p.real*q,p.imag*q);
        } 
        void operator *= (com q){
            *this=(*this)*q;
        }
        void operator *= (double q){
            *this=(*this)*q;
        }
        friend com operator / (com p,double q){
            return com(p.real/q,p.imag/q);
        } 
        void operator /= (double q){
            *this=(*this)/q;
        } 
        void print(){
            printf("%lf + %lf i ",real,imag);
        }
    };
    void fft(com *x,int n,int type){
    	static int rev[maxn+5];
    	int dn=1,k=0;
    	while(dn<n){
    		dn*=2;
    		k++;
    	}
    	for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
    	for(int i=0;i<n;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
    	for(int len=1;len<n;len*=2){
    		int sz=len*2;
    		com wn1=com(cos(2*pi/sz),sin(2*pi/sz)*type);
    		for(int l=0;l<n;l+=sz){
    			int r=l+len-1;
    			com wnk=1;
    			for(int i=l;i<=r;i++){
    				com tmp=x[i+len];
    				x[i+len]=x[i]-wnk*tmp;
    				x[i]=x[i]+wnk*tmp;
    				wnk*=wn1;
    			}
    		}
    	}
    	if(type==-1) for(int i=0;i<n;i++) x[i]/=n;
    }
    void mul(com *a,com *b,com *ans,int n){
    	fft(a,n,1);
    	if(a!=b) fft(b,n,1);
    	for(int i=0;i<n;i++) ans[i]=a[i]*b[i];
    	fft(ans,n,-1);
    }
    
    int n;
    int bsz,bcnt;
    int a[maxn+5]; 
    int bel[maxn+5];
    int lb[maxn+5],rb[maxn+5];//预处理每个数的左右边界,卡常 
    void ini_block(){
    	bsz=sqrt(n*log(n)/log(2));
    	bcnt=1;
    	for(int i=1;i<=n;i++){
    		bel[i]=bcnt;
    		if(i%bsz==0) bcnt++;
    	}
    	for(int i=1;i<=bcnt;i++){
    		lb[i]=bsz*(i-1)+1;
    		rb[i]=min(i*bsz,n); 
    	}
    } 
    
    int cntl[maxn+5],cntr[maxn+5];
    com fl[maxn+5],fr[maxn+5],res[maxn+5];
    int main(){
    	scanf("%d",&n);
    	ini_block();
    	int maxv=0;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		maxv=max(maxv,a[i]);
    	} 
    	//i,j,k有>=2个在同一块内,直接暴力O(n/B*B^2)=O(Bn)
    	ll ans=0;
    	for(int i=n;i>=1;i--) cntr[a[i]]++;
    	for(int id=1;id<=bcnt;id++){
    		//cntl记录的是当前块前面的数的出现情况
    		//cntr记录的是i后面的数的出现情况
    		//注意i,j,k均在一个块内的情况只能被算1次  
    		for(int k=lb[id];k<=rb[id];k++){
    			cntr[a[k]]--;
    			for(int j=lb[id];j<k;j++){
    				int ai=a[j]*2-a[k];
    				if(ai>=0&&ai<=maxn) ans+=cntl[ai]; 
    				ai=a[k]*2-a[j];
    				if(ai>=0&&ai<=maxn) ans+=cntr[ai];//这样只有这个时候会算到i,j,k在同一个块里的情况 
    			}
    			
    		}
    		for(int j=lb[id];j<=rb[id];j++) cntl[a[j]]++;
    	} 
    	memset(cntl,0,sizeof(cntl));
    	memset(cntr,0,sizeof(cntr));
    	for(int i=n;i>=lb[2];i--) cntr[a[i]]++;
    	for(int i=1;i<=rb[1];i++) cntl[a[i]]++; 
    	for(int id=2;id<bcnt;id++){
    		for(int j=lb[id];j<=rb[id];j++) cntr[a[j]]--;
    		for(int j=0;j<=maxv;j++){
    			fl[j]=cntl[j];
    			fr[j]=cntr[j];
    		}
    		int dn=1;
    		while(dn<=maxv*2) dn*=2;
    		mul(fl,fr,res,dn);
    		for(int j=lb[id];j<=rb[id];j++){
    			ans+=(ll)(res[a[j]*2].real+0.5);
    		}
    		for(int j=0;j<=dn;j++){
    			fl[j]=0;
    			fr[j]=0;
    			res[j]=0;
    		}
    		for(int j=lb[id];j<=rb[id];j++) cntl[a[j]]++;
    	} 
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    021.day21 反射 Class类 反射常用操作
    020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步
    019.day19 缓冲流 对象流 标准输入输出流
    018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码
    017.day17 Map接口 克隆 treeSet集合排重缺陷
    016.day16 HashSet TreeSet 比较器Comparable Comparator
    015.day15
    014.day14
    013.day13
    线程
  • 原文地址:https://www.cnblogs.com/birchtree/p/11715737.html
Copyright © 2011-2022 走看看