Codeforces Round #Pi (Div. 2) Problem C

Codeforces Round #Pi (Div. 2) Problem C
题目大意:给你n个数a[1],a[2],……a[n] (109a[i]109) ,和一个k( 1n,k2105 ),在这n个数中找到3个数,a[i], a[j], a[x],使这三个数成等比数列,公比是k,且三个数的下标: 1<=i<j<x<=n ,三个数不必连续,问能够找到多少这样的组合。
既然是等比数列则有: a[i]=a[j]/ka[j]=a[x]/ka[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)

你可能感兴趣的:(枚举,codeforces,Pi-Div2,Problem-C)