Evanyou Blog 彩带

2次幂的个数

题目描述

给定一个长度为n的正整数序列 $ a[i] $ ,计算出有多少个i<j的数对, $ a[i]+a[j] $ 为二的次幂,也就是说存在一个正整数 $ x $ 满足 $ a[i]+a[j]==2^x $ 。

输入

输入文件A.in。

第一行一个整数n。
第二行n个整数,其中第i个整数为a[i]。

输出

输出文件A.out。

一行一个整数表示数对的数量。

样例输入

4
7 3 2 1

样例输出

2

【样例输入2】

3
1 1 1

【样例输出2】

3

【数据范围】

对于 20% 数据 $ n≤10^3 $
对于 50% 数据 $ n≤5×10^4,0≤ai≤10 ^9 $
对于 100% 数据 $ n≤10 ^6 ,0≤ai≤10 ^9 $

solution :

$ p[i] $ : 预先处理出 $ 2^i $ ,因为数据范围小于 $ 10 ^9 $ ,所以就处理到三十就好了。

第一次指针:找到 $ 2 ^i $ 下每种情况。
第二次指针:找到在每种情况下有多少种重复的情况,然后用乘法原理一次就处理出来。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
#define re register
using namespace std ;
const int maxn = 1000005 ;

inline int read () {
	int f = 1 , x = 0 ;
	char ch = getchar () ;
	while(ch > '9' || ch < '0')  {if(ch == '-') f = -1 ; ch = getchar () ;}
	while(ch >= '0' && ch <= '9')  {x = (x << 1) + (x << 3) + ch - '0' ; ch = getchar () ;}
	return x * f; 
}

int n , a[maxn] , p[maxn] ;
long long ans ;

int main () {
	n = read () ;
	p[0] = 1 ;
	for(re int i = 1 ; i <= n ; ++ i)
		a[i] = read() ;
	for(re int i = 1 ; i <= 30 ; ++ i)
		p[i] = p[i - 1] << 1 ;
	sort(a + 1 , a + 1 + n) ;
	for(re int j = 30 ; j >= 0 ; -- j) {
		int l = 1 , r = n ;
		while(l < r) {
			while(a[l] + a[r] > (long long)p[j])  -- r ;
			while(a[l] + a[r] < (long long)p[j])  ++ l ;
			if(l >= r)  break ;
			if(a[l] == a[r]) {
				if(a[l] + a[r] == (long long)p[j]) 
					ans += (long long)(r - l + 1) * (r - l) / 2 ;
					break ;
			}
			int ll = l , rr = r ;
			long long sum1 = 0 , sum2 = 0 ;
			if(a[ll] + a[rr] == (long long)p[j]) {
				while(a[ll] == a[l])  ++sum1 , ++ ll ;
				while(a[rr] == a[r])  ++sum2 , ++ rr ;
			}
			ans += sum1 * sum2 ;
			l = ll , r = rr ;
		}
	}
	printf("%lld\n" , ans) ;
	return 0 ;
}
posted @ 2019-05-30 21:03  Stephen_F  阅读(808)  评论(0编辑  收藏  举报