CodeChef COUNTARI Arithmetic Progressions(分块 + FFT)

题目

Source

http://vjudge.net/problem/142058

Description

Given N integers A1, A2, …. AN, Dexter wants to know how many ways he can choose three numbers such that they are three consecutive terms of an arithmetic progression.

Meaning that, how many triplets (i, j, k) are there such that 1 ≤ i < j < k ≤ N and Aj - Ai = Ak - Aj.

So the triplets (2, 5, 8), (10, 8, 6), (3, 3, 3) are valid as they are three consecutive terms of an arithmetic
progression. But the triplets (2, 5, 7), (10, 6, 8) are not.

Input

First line of the input contains an integer N (3 ≤ N ≤ 100000). Then the following line contains N space separated integers A1, A2, …, AN and they have values between 1 and 30000 (inclusive).

Output

Output the number of ways to choose a triplet such that they are three consecutive terms of an arithmetic progression.

Sample Input

10
3 5 3 6 3 4 10 4 5 2

Sample Output

9

分析

题目大概说给一个长N的序列A,问有多少个三元组<i,j,k>满足i<j<k且Ai+Ak=2Aj。

i<j<k这个关系不好搞,正解好像是分块:

  • 把序列分解成若干块,每一块长度大约为B。分三种情况考虑:
  1. 对于三个都在同一块的:枚举各个块,然后通过枚举i和k并更新记录j的信息求出对数。时间复杂度$O(N/B\times B\times B)=O(NB)$。
  2. 对于只有两个在同一块的:枚举各个块,并更新记录前面所有块和后面所有块的信息,然后枚举块内的两个数,另一个数可能在块前也可能在块后,这样求出对数。时间复杂度$O(N/B\times B\times B)=O(NB)$。
  3. 对于三个数都在不同块的:枚举各个块,并更新记录前面所有块和后面所有块的信息,然后构造多项式用FFT求出前后两边组合成各个和的方案数,通过枚举块内的j即可求出对数。时间复杂度$O(N/B\times 65535\times 16+N/B\times B)$
  • 然后就是B大小的设定,我设定的是$5\sqrt N$。
  • 最后我连枚举都不会枚举。。还有有个地方只考虑是否大于0,没考虑是否小于等于30000,数组越界,WA了好久。。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF (1<<30)
#define MAXN 66666
const double PI=acos(-1.0);

struct Complex{
	double real,imag;
	Complex(double _real,double _imag):real(_real),imag(_imag){}
	Complex(){}
	Complex operator+(const Complex &cp) const{
		return Complex(real+cp.real,imag+cp.imag);
	}
	Complex operator-(const Complex &cp) const{
		return Complex(real-cp.real,imag-cp.imag);
	}
	Complex operator*(const Complex &cp) const{
		return Complex(real*cp.real-imag*cp.imag,real*cp.imag+cp.real*imag);
	}
	void setValue(double _real=0,double _imag=0){
		real=_real; imag=_imag;
	}
};

int len;
Complex wn[MAXN],wn_anti[MAXN];

void FFT(Complex y[],int op){
	for(int i=1,j=len>>1,k; i<len-1; ++i){
		if(i<j) swap(y[i],y[j]);
		k=len>>1;
		while(j>=k){
			j-=k;
			k>>=1;
		}
		if(j<k) j+=k;
	}
	for(int h=2; h<=len; h<<=1){
		Complex Wn=(op==1?wn[h]:wn_anti[h]);
		for(int i=0; i<len; i+=h){
			Complex W(1,0);
			for(int j=i; j<i+(h>>1); ++j){
				Complex u=y[j],t=W*y[j+(h>>1)];
				y[j]=u+t;
				y[j+(h>>1)]=u-t;
				W=W*Wn;
			}
		}
	}
	if(op==-1){
		for(int i=0; i<len; ++i) y[i].real/=len;
	}
}
void Convolution(Complex A[],Complex B[],int n){
	for(len=1; len<(n<<1); len<<=1);
	for(int i=n; i<len; ++i){
		A[i].setValue();
		B[i].setValue();
	}

	FFT(A,1); FFT(B,1);
	for(int i=0; i<len; ++i){
		A[i]=A[i]*B[i];
	}
	FFT(A,-1);
}

int a[111111];
int cnt[33100],cnt0[33100],cnt1[33100];
Complex A[MAXN],B[MAXN];

int main(){
	for(int i=0; i<MAXN; ++i){
		wn[i].setValue(cos(2.0*PI/i),sin(2.0*PI/i));
		wn_anti[i].setValue(wn[i].real,-wn[i].imag);
	}
	int n;
	while(~scanf("%d",&n)){
		for(int i=0; i<n; ++i){
			scanf("%d",a+i);
		}
		int block=(int)(sqrt(n)+1e-6)*5;

		long long ans=0;

		for(int b=0; b<n; b+=block){
			for(int i=0; i<block && b+i<n; ++i){
				for(int j=i+1; j<block && b+j<n; ++j){
					int tmp=a[b+i]+a[b+j];
					if((tmp&1)==0){
						ans+=cnt[tmp>>1];
					}
					++cnt[a[b+j]];
				}
				for(int j=i+1; j<block && b+j<n; ++j){
					--cnt[a[b+j]];
				}
			}
		}

		memset(cnt0,0,sizeof(cnt0));
		memset(cnt1,0,sizeof(cnt1));
		for(int i=0; i<n; ++i){
			++cnt1[a[i]];
		}
		for(int b=0; b<n; b+=block){
			for(int i=0; i<block && b+i<n; ++i){
				--cnt1[a[b+i]];
			}
			for(int i=0; i<block && b+i<n; ++i){
				for(int j=i+1; j<block && b+j<n; ++j){
					int tmp=a[b+i]*2-a[b+j];
					if(tmp>0 && tmp<=30000) ans+=cnt0[tmp];
					tmp=a[b+j]*2-a[b+i];
					if(tmp>0 && tmp<=30000) ans+=cnt1[tmp];
				}
			}
			for(int i=0; i<block && b+i<n; ++i){
				++cnt0[a[b+i]];
			}
		}

		memset(cnt0,0,sizeof(cnt0));
		memset(cnt1,0,sizeof(cnt1));
		for(int i=0; i<n; ++i){
			++cnt1[a[i]];
		}
		for(int b=0; b<n; b+=block){
			for(int i=0; i<block && b+i<n; ++i){
				--cnt1[a[b+i]];
			}

			for(int i=0; i<=30000; ++i){
				A[i].setValue(cnt0[i]);
				B[i].setValue(cnt1[i]);
			}
			Convolution(A,B,30001);
			for(int i=0; i<block && b+i<n; ++i){
				ans+=(long long)(A[a[b+i]<<1].real+0.5);
			}

			for(int i=0; i<block && b+i<n; ++i){
				++cnt0[a[b+i]];
			}
		}

		printf("%lld\n",ans);
	}
	return 0;
}
时间: 2024-10-10 21:44:29

CodeChef COUNTARI Arithmetic Progressions(分块 + FFT)的相关文章

CodeChef - COUNTARI Arithmetic Progressions (FFT)

题意:求一个序列中,有多少三元组$(i,j,k)i<j<k $ 满足\(A_i + A_k = 2*A_i\) 构成等差数列. https://www.cnblogs.com/xiuwenli/p/9719425.html 在该题的基础上加了i<j<k的限制.不能单纯地FFT枚举结果. 考虑分块搭配FFT结果问题. 将原序列分成若干块,设每个块的大小为\(B\).则构成等差数列的三元组有三种构成情况. 1)三个数都在一个块内.这种情况对每个块独立处理.二重循环枚举\(A_i .A_

CC countari &amp; 分块+FFT

题意: 求一个序列中顺序的长度为3的等差数列. SOL: 对于这种计数问题都是用个数的卷积来进行统计.然而对于这个题有顺序的限制,不好直接统计,于是竟然可以分块?惊为天人... 考虑分块以后的序列: 一个块内直接枚举统计三个或两个在块内的. 只有一个在当前块我们假设它是中间那个,对左右其它块做卷积. 但是还是感觉复杂度有点玄学啊... 我比较傻逼...一开始块内统计根本没有想清楚...最后做卷积硬生生把复杂度变成了 $\sqrt{N}*N*log(N)$... 改了一个晚上终于没忍住看标程...

CodeChef - COUNTARI FTT+分块

Arithmetic Progressions Given N integers A1, A2, …. AN, Dexter wants to know how many ways he can choose three numbers such that they are three consecutive terms of an arithmetic progression. Meaning that, how many triplets (i, j, k) are there such t

poj 3006 Dirichlet&#39;s Theorem on Arithmetic Progressions

Description If a and d are relatively prime positive integers, the arithmetic sequence beginning with a and increasing by d, i.e., a, a + d, a + 2d, a + 3d, a + 4d, ..., contains infinitely many prime numbers. This fact is known as Dirichlet's Theore

(素数求解)I - Dirichlet&#39;s Theorem on Arithmetic Progressions(1.5.5)

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Description If a and d are relatively prime positive integers, the arithmetic sequence beginning with a and increasing by d, i.e., a, a + d, a + 2d, a + 3d, a 

POJ 3006 Dirichlet&#39;s Theorem on Arithmetic Progressions 素数 难度:0

http://poj.org/problem?id=3006 #include <cstdio> using namespace std; bool pm[1000002]; bool usd[1000002]; bool judge(int x) { if(usd[x])return pm[x]; usd[x] = true; if(x == 2) return pm[x] = true; if(((x & 1) == 0) || (x < 2))return pm[x] =

洛谷P1214 [USACO1.4]等差数列 Arithmetic Progressions

P1214 [USACO1.4]等差数列 Arithmetic Progressions• o 156通过o 463提交• 题目提供者该用户不存在• 标签USACO• 难度普及+/提高 提交 讨论 题解 最新讨论• 这道题有问题• 怎么进一步优化时间效率啊 …题目描述一个等差数列是一个能表示成a, a+b, a+2b,..., a+nb (n=0,1,2,3,...)的数列.在这个问题中a是一个非负的整数,b是正整数.写一个程序来找出在双平方数集合(双平方数集合是所有能表示成p的平方 + q的平

USACO 1.4 Arithmetic Progressions

Arithmetic Progressions An arithmetic progression is a sequence of the form a, a+b, a+2b, ..., a+nb where n=0,1,2,3,... . For this problem, a is a non-negative integer and b is a positive integer. Write a program that finds all arithmetic progression

BZOJ 3509 分块FFT

思路: 跟今年WC的题几乎一样 (但是这道题有重 不能用bitset水过去) 正解:分块FFT http://blog.csdn.net/geotcbrl/article/details/50636401    from GEOTCBRL 可以看看hgr的题解..写得很详细 //By SiriusRen #include <cmath> #include <cstdio> #include <cstring> #include <algorithm> usi