CF561做题

C题:

一期思路:我们发现如果x,y满足条件,那么{x,-y} {-x,y} {-x,-y}也满足条件。那么我们可以只讨论|x| |y|是否满足条件,如果满足条件,那么对ans的贡献是|x|出现次数*|y|出现次数。

于是引申出用二分查找满足条件的值的最靠后的位置,用map保存出现的次数,因为你会发现元素的范围在-10亿到10亿,不能开线性数组来保存出现次数。

查找到最靠后的位置之后,

for(j=i+1;j<=ans_pos;++j)  ans = ans + cnt[base]*cnt[ v[j] ];
但是这样的坏处是

(1)使用了map,map需要查找时间

(2)每个都要遍历一遍,复杂度到了O(n^2)。

(3)容易忽略绝对值相等的情况

  因为一个-x和一个x是能满足条件的,而这个公式很显式的把这种情况排除了,思路就被带偏了。

 综上,即使我解决了(3),if(cnt[base]==2) ++ans;

  还是会因为复杂度到了O(n^2)而超时,那么这时候题解的思路就很好了,压入的是所有的绝对值,这样不仅节约时间,还保证了能考虑到绝对值相等也能满足题意的情况。

思路正确但是TLE的代码:

#include <bits/stdc++.h>
#define pt printf
#define sc scanf
#define maxn 200005
#define ull  unsigned  long long 
#define inf 0x3f3f3f3f
using namespace std;
int N;
int a[maxn];
vector<int> v;
map<int,int> cnt;
int can(int where,int base)
{
    int val =  v[where] ;
    if(val%2==0)
    {
        if(base>=val/2) return 1;
        return 0;
    }
    else
    {
        if(base> val/2) return 1;
        return 0;
    }
}
int main()
{
    sc("%d",&N);
    int i,j,x;
    for(i=0;i<N;++i) sc("%d",&a[i]);
    for(i=0;i<N;++i)
    {
        x = abs(a[i]);
        if(cnt.find(x)==cnt.end())
        {
            cnt[x]=1;
            v.push_back(x);
        } 
        else ++cnt[x];
    }
    sort(v.begin(), v.end());

    int len = v.size();
//    for(i=0;i<len;++i) pt("cnt[v[i]]=%d ",cnt[v[i]]);
//        pt("
");
    ull ans = 0;
    //假设大的数是b,小的数是a
    //如果b是偶数,如果a大于等于b的一半那就可以
    //如果b是奇数,如果a大于b的一半那就可以
    for(i=0;i<len;++i)
    {
//        pt("i=%d
",i);
        int base = v[i];
        int l = i+1, r = len - 1, ans_pos = i ;
        //pt("base=%d ,l=%d ,r=%d ,ans_pos=%d 
",base,l,r,ans_pos);
        while(l<=r)
        {
            int mid  =  (l+r)>>1;
            int status = can(mid,base);
            if(status==1) 
            {
                ans_pos = mid;
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        for(j=i+1;j<=ans_pos;++j)
        {
            ans = ans + cnt[base]*cnt[ v[j] ];
        }
        if(cnt[base]==2) ++ans;
    }
    pt("%llu
",ans);
    return 0;
}
View Code

AC的代码:满足条件的函数我分了奇偶讨论,但是只要   小的数*2>=大的数   就可以了。

#include <bits/stdc++.h>
#define pt printf
#define sc scanf
#define maxn 200005
#define ull  unsigned  long long 
#define inf 0x3f3f3f3f
using namespace std;
int N;
int a[maxn];
vector<int> v;
int can(int where,int base)
{
    int val =  v[where] ;
    if(val%2==0)
    {
        if(base>=val/2) return 1;
        return 0;
    }
    else
    {
        if(base> val/2) return 1;
        return 0;
    }
}
int main()
{
    sc("%d",&N);
    int i,j,x;
    for(i=0;i<N;++i) sc("%d",&a[i]);
    for(i=0;i<N;++i)
    {
        x = abs(a[i]);
        v.push_back(x);
    }
    sort(v.begin(), v.end());
    int len = N;
//    for(i=0;i<len;++i) pt("cnt[v[i]]=%d ",cnt[v[i]]);
//        pt("
");
    ull ans = 0;
    //假设大的数是b,小的数是a
    //如果b是偶数,如果a大于等于b的一半那就可以
    //如果b是奇数,如果a大于b的一半那就可以
    for(i=0;i<len;++i)
    {
//        pt("i=%d
",i);
        int base = v[i];
        int l = i+1, r = len - 1, ans_pos = i ;
        //pt("base=%d ,l=%d ,r=%d ,ans_pos=%d 
",base,l,r,ans_pos);
        while(l<=r)
        {
            int mid  =  (l+r)>>1;
            int status = can(mid,base);
            if(status==1) 
            {
                ans_pos = mid;
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        ans += (ans_pos-i);
    }
    pt("%llu
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lighten-up-belief/p/10885414.html