BSOJ4751题解

来一个奇怪做法。

分块,设块长 \(B\),先块内数三元组,复杂度 \(nB\)

块内部和外部的可以做一个卷积来求,复杂度 \(\frac{n}{B}\times V\log V\)

要让 \(O(nB+\frac{nV\log V}{B})\) 最小,取 \(B=\sqrt{V\log V}\),得到 \(O(n\sqrt{V\log V})\),可以通过。

需要注意常数。

\(998244353\) 好像没被卡,奇怪。

#include<cstdio>
#include<cctype>
#define IMP(n,act) for(int lim=(n),i=0;i^lim;++i)act
const int M=1e5+5,mod=998244353,Block=4000,T=1<<16;
int n,a[M];int len,p[Block];
int A[T|1],B[T|1],F[30][T|1],tmp[T|1];
int X[30005],Y[30005],Q[30005],P[30005];
int buf[1<<18|1],*w[M];bool vis[30005];
long long ans;
inline void swap(int&a,int&b){
	int c=a;a=b;b=c;
}
inline int Add(const int&a,const int&b){
	return a+b>=mod?a+b-mod:a+b;
}
inline int Del(const int&a,const int&b){
	return b>a?a-b+mod:a-b;
}
inline int Getlen(const int&n){
	int len(0);while((1<<len)<n)++len;return len;
}
inline int pow(int a,int b=mod-2){
	int ans(1);for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;return ans;
}
inline void init(const int&n){
	int m=Getlen(n),*W=buf;w[m]=W;W+=1<<m;w[m][0]=1;w[m][1]=pow(3,mod-1>>m+1);
	for(int i=2;i^1<<m;++i)w[m][i]=1ll*w[m][i-1]*w[m][1]%mod;
	for(int k=m-1;k>=0&&(w[k]=W,W+=1<<k);--k)IMP(1<<k,w[k][i]=w[k+1][i<<1]);
}
inline void DFT(int*f,const int&M){
	const int&n=1<<M;
	for(int d=M-1,len=n>>1;d>=0;--d,len>>=1){
		for(int k=0;k^n;k+=len<<1){
			int*W=w[d],*fl=f+(k),*fr=f+(k|len),x,y;
			IMP(len,(x=*fl,y=*fr)),*fl++=Add(x,y),*fr++=1ll**W++*Del(x,y)%mod;
		}
	}
}
inline void IDFT(int*f,const int&M){
	const int&n=1<<M;
	for(int d=0,len=1;d<M;++d,len<<=1){
		for(int k=0;k^n;k+=len<<1){
			int*W=w[d],*fl=f+(k),*fr=f+(k|len),x,y;
			IMP(len,(x=*fl,y=1ll**W++**fr%mod)),*fl++=Add(x,y),*fr++=Del(x,y);
		}
	}
	const int&k=pow(n);IMP(n,f[i]=1ll*f[i]*k%mod);for(int i=1;(i<<1)<n;++i)swap(f[i],f[n-i]);
}
inline int read(){
	int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline void write(int n){
	static char s[10];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);
}
inline void Solve(const int&i,const int&L,const int&R){
	len=0;
	for(int i=L;i<=R;++i)++P[a[i]],--Y[a[i]];for(int k=0;k^T;++k)B[k]=Del(B[k],F[i][k]);
	for(int i=L;i<=R;++i){
		--P[a[i]];
		for(int k=1;k<=len;++k)if(a[i]*2-p[k]>=1&&a[i]*2-p[k]<=30000)ans+=Q[p[k]]*P[a[i]*2-p[k]];
		++Q[a[i]];if(!vis[a[i]])vis[p[++len]=a[i]]=true;
	}
	for(int i=L;i<=R;++i)vis[a[i]]=false;
	for(int k=0;k^T;++k)tmp[k]=1ll*A[k]*B[k]%mod;IDFT(tmp,16);for(int i=L;i<=R;++i)ans+=tmp[a[i]<<1];
	for(int i=L;i<=R;++i)for(int j=i+1;j<=R;++j){
		if(a[i]*2-a[j]>=1&&a[i]*2-a[j]<=30000)ans+=X[a[i]*2-a[j]];
		if(a[j]*2-a[i]>=1&&a[j]*2-a[i]<=30000)ans+=Y[a[j]*2-a[i]];
	}
	for(int i=L;i<=R;++i)--Q[a[i]],++X[a[i]];for(int k=0;k^T;++k)A[k]=Add(A[k],F[i][k]);
}
signed main(){
	n=read();init(1<<16);
	for(int i=1;i<=n;++i)++F[(i-1)/Block+1][a[i]=read()];
	for(int i=1;i<=n;++i)++Y[a[i]];
	for(int i=1;(i-1)*Block<n;++i){
		DFT(F[i],16);for(int k=0;k^T;++k)B[k]=Add(B[k],F[i][k]);
	}
	for(int i=1;(i-1)*Block<n;++i)Solve(i,(i-1)*Block+1,i*Block>n?n:i*Block);printf("%lld",ans);
}
posted @ 2022-06-22 19:07  Prean  阅读(18)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};