Codeforces Round #Pi (Div. 2) Problem C
题目大意:给你n个数a[1],a[2],……a[n]
(− 109 ≤ a[i] ≤ 109)
,和一个k(
1 ≤ n, k ≤ 2⋅105
),在这n个数中找到3个数,a[i], a[j], a[x],使这三个数成等比数列,公比是k,且三个数的下标:
1<=i<j<x<=n
,三个数不必连续,问能够找到多少这样的组合。
既然是等比数列则有:
a[i]=a[j]/k,a[j]=a[x]/k,a[i]=a[x]/k/k
。
很容易想到的一种最简单暴力的一种做法就是直接枚举3个数的位置,时间复杂度
O(n3)
,明显超时,需要一种高效的算法。
可以这样想,去枚举三个数中间的那个数,设为a[j],既然是中间的那个数,那么这个a[j] % k=0,在j之前找到
a[i]=a[j]/k
,记这个a[i]个数为cnt1个,在j之后找到
a[x]=a[j]∗k
,记个a[x]个数为cnt2,那么以a[j]为中间的那个数,就能够得到cnt1*cnt2中组个使得这三个数成等比数列,并且公比是k,且下标是严格递增的。
有了这个想法之后,想想还是得枚举中间的a[j],在去前面找a[i],后面找a[x],时间复杂度还是没变啊,其实可以巧妙地用一种方法。
C++的STL中有两个库函数lower_bound(),upper_bound()。lower_bound(begin,end,val)用于在数组或者容器中,范围是[begin,end)找到一个最小可以插入val的位置,upper_bound(begin,end,val)则是找到一个最大可以插入的位置。
所以,可以这样处理,将每个数记录其初始的位置,然后以其值排序,就可以通过lower_bound()和upper_bound(),来高效地枚举了。
注意爆int啊啊啊啊。
代码如下,仅供参考:
/*
Author:Royecode
Date:2015-08-06
*/
#include <bits/stdc++.h>
#define Pii pair <ll, int>
#define ll long long
using namespace std;
const int MAXN = 200007;
Pii arr[MAXN];
int main()
{
ll n, k;
while(~scanf("%I64d%I64d", &n, &k))
{
for(int i = 0; i < n; ++i)
{
scanf("%I64d", &arr[i].first);
arr[i].second = i;
}
sort(arr, arr + n);
ll ans = 0;
for(int i = 1; i < n - 1; ++i)
{
if(arr[i].first % k == 0)//枚举中间的数
{
//arr[i]/k的个数
ll cnt1 = upper_bound(arr, arr + n, Pii(arr[i].first / k, arr[i].second - 1)) - lower_bound(arr, arr + n, Pii(arr[i].first / k, 0));
//arr[i]×k的个数
ll cnt2 = upper_bound(arr, arr + n, Pii(arr[i].first * k, n)) - lower_bound(arr, arr + n, Pii(arr[i].first * k, arr[i].second + 1));
ans += cnt1 * cnt2;
}
}
printf("%I64d\n", ans);
}
return 0;
}
/*
input:
5 2
1 1 2 2 4
10 3
1 2 6 2 3 6 9 18 3 9
output:
4
6
*/
时间复杂度: O(nlogn) 。