UVA 11401 Triangle Counting
【题意分析】
本题就是在给定的N条边(边长是1,2,3,,,N)里面找合乎要求的三角形个数(任意两条边之和大于第三边)。如果我们直接枚举合乎题意的三角形那么我们不太好出发(想想为什么?),那么我们可以采用补集观念,先找出不合乎要求的三角形数目,再用总的组合数减去不合乎要求的数目,就得最后的结果。那么问题是我们如何枚举不合乎要求的三角形数目呢?我们设三角形的三条边是a,b,c。我们不妨用c表示任意三条边中的边长最大值,那么我们可以得到一个简单的结论:如果能够构成三角形c<a+b.那么当用a和b作为边时,不符合要求的种数为:N-(a+b)+1(此时c>=a+b)。我们不妨令i=a+b。那么对于每一个i,a与b的组合方式有(i-1)/2种方式(仔细想想可以枚举一下有规律)。那么我们可以得到不符要求的组合方式有:(N-(i)+1)*((i-1)/2)种(3<=i<=N).考虑到本题的N可能很大,我们必须用long long 存储数据。如果直接按照得到的公式从3到N进行相加,那么一定会超时。我们可以通过数学化简的形式简化计算过程使得算法的时间为O(1)。那么如何解决呢?
我们需要考虑i为奇偶数的情况:
当i为奇数(i=2*k+1)时(1<=k<=(N-1)/2),我们可以公式1计算:
N * sum(n) - 2 * sum2(n);
当i为偶数时(i=2*k+2)时(1<=k<N/2),我们可以采用公式2计算(这里有变换技巧注意体会):
(N + 3) * sum(n) - 2 * sum2(n) - (LL)(N + 1) * n;
其中:
typedef long long LL; int N; LL sum(int n) { return (LL)n * (n + 1) / 2; } LL sum2(int n) { return (LL) n * (n + 1) * (2 * n + 1) / 6; }
到这里我们用CN3 减去不符合要求的就行了。
【AC代码】
#include<stdio.h> #include<string.h> typedef long long LL; int N; LL sum(int n) { return (LL)n * (n + 1) / 2; } LL sum2(int n) { return (LL) n * (n + 1) * (2 * n + 1) / 6; } int main() { while(scanf("%d", &N), N >= 3) { LL ans = (LL)N * (N - 1) * (N - 2) / 6; int n = N / 2; ans -= (N + 3) * sum(n) - 2 * sum2(n) - (LL)(N + 1) * n; n = (N - 1) / 2; ans -= N * sum(n) - 2 * sum2(n); printf("%lld\n", ans); } return 0; }