【Codechef】Chef and Triangles -Problem Code: MAKETRI

题面:

  小明已经厌倦了做菜,做菜对他来说太简单了。这天,他决定要挑战一下自己。 他已经选择了 N 根意大利面,准备做一道菜。他还需要选出一根意大利面,使得这根意大利面可以和已有的 N 根面中的某两根构成一个三角形。选出的意大利面的长度必须在 [L, R] 的范围内。 请你求出小明选择的方案数

输入:

  第一行给出N,L,R。 第二行给出N个数len1,len2,...,lenN,表示N个意大利面的长度。

  2<=N<=1e6 1<=L,R<=1e18,1<=leni<=1e18

输出:

  输出方案数。

样例:

Input:
5 1 4
1 2 3 4 5
Output:
3

这题卡了我很久。毕竟我之前没做过这一类的题目。

题意还是较为简单,不过就是从[L,R]的范围内能选出多少条条边与给出数组中的数凑成三角形
看到数据规模自然是不能暴力的,毕竟O(n2)加上1e6的规模必然会炸。

首先毕竟题目是要凑三角形,我们必然就能想到三角形的性质:
1.任意两边之和大于第三边
2.任意两边之差小于第三边

我们假设我们在[L,R]内取了x长度的边,之后又从给出的长度数组A中取了Ai与Aj长度的边。
易得到以下的式子:
·x + Ai > Aj ——> x > Aj - Ai
·x + Aj > Ai ——> x > Ai - Aj
·Ai + Aj > x ——> x < Ai + Aj
关于为什么是这三条式子,其实你就算写出了其他的也能化成这三条,我只是为了方便就拿了这样的式子。

所以我们就能很自然地想出,[L,R]与[max(Ai-Aj, Aj-Ai)+1, Ai+Aj-1]交集即为选取Ai与Aj的x的方案数。
上面的交集的意思就是既要在[L,R]范围内又要能与Ai和Aj组成三角形的范围。

但是关于怎么选取Ai与Aj确仍然是个问题。毕竟暴力的话还是O((R-L+1)*n2)。
这里引入一个很重要的结论:

假设这里有三个数A1 < A2 < A3,我们定义R(i,j)代表的是Ai,Aj能形成的三角形的范围。
所以
  R(1,2)=[A2-A1+1,A2+A1-1],
  R(2,3)=[A3-A2+1,A3+A2-1]
  R(1,3)=[A3-A1+1,A3+A1-1]。
R(2,3)是完全包含R(1,3)的。这个也是较为好证明的,毕竟两个更大更相近的数所形成的三角形的范围,必然是要比两个较小相距较远的数来的大的。
(具体可以自己举例证明)

有了这个结论后我们就可以先将A数组排序,之后通过n-1对R(j-1,j)的并集(1-n总共有n-1个j与j-1),再与[L,R]的交集即可以求出所求的方案数。
不过千万要注意特殊的情况,不然会WA很多发。

以下为AC代码:
【Codechef】Chef and Triangles -Problem Code: MAKETRI
#include<bits/stdc++.h>
#define ll long long
using namespace std;

int main()
{
    long long n,l,r;
    scanf("%lld %lld %lld",&n,&l,&r);

    ll a[n];

    for(ll i=0;i<n;i++)
    scanf("%lld",&a[i]);
    sort(a,a+n);

    ll sum,diff,ans=0;
    if (l > r)
    {
        cout << ans << endl;
        return 0;
    }

    for(ll i=n-1;i>0;i--)
    {
        sum = a[i]+a[i-1]-1;
        diff = a[i]-a[i-1]+1;
        //这里直接求交集
        ll s = max(l,diff);
        ll d = min(sum,r);
        if (s <= d)
            ans += d-s+1;
        //把选用过的长度去掉
        r = min(r,diff-1);
    }

    printf("%lld\n",ans);

    return 0;
}
View Code
上一篇:python-Chef,ec2,knife的最佳做法,并在发生故障转移时减少启动和AMI的时间


下一篇:如何判断Chef客户端是否安装在Linux上?