Codeforces Round #317 A. Lengthening Sticks(组合+容斥)

之前也遇到过好几次这种类型的题目了, 但是这次还是没有在比赛中做出来。

因为输入只有4个数, 可变化的状态很少, 但是数据范围又很大, 所以不可能是DP之类的, 一定是一道数学题。   这种特点的题目我们很容易想到排列组合, 然而排列组合往往又会带来重复计数的问题, 所以往往这类题目又需要夹杂容斥定理, 可以说是约定俗称的题目类型。

那么不难想到, 用总的组合情况减去不成立的情况(不成立的情况往往好求)。   对于总的情况, 我们不妨从0枚举到l,枚举三根木棒增加长度的总和i。 那么我们可以将其想象成一根长i的木棒,要将其分成3份。  就行在木棒上切两刀。 但是由于增加的长度可以为0, 所以两刀可以切在一点。   例如: i = 5   那么如果两刀都切在3上, 三根木棒分别增加3、0、2;  所以假设第一刀切在0点, 那么第二刀有l+1种可能:0、1、2.....l 。假设第一刀在1,那么第二刀有l种可能, 以此类推。。。 所以总的方案数为(l+1)+l+(l-1)+(l-2)+......+1。一共(l+2)*(l+1)/2种可能。

接下来排除不可能的情况。  我们不妨假设a+i最长(i=0,1,2....l)。 那么该边最长,我们可以算出来该边和其他两边和的差值,那么这一段长度是可以像一开始一样求组合数的,因为无论怎么组合,a+i仍然是最长的。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
ll a,b,c,l;
ll solve(ll a, ll b, ll c) {
    ll cur = 0;
    for(ll i=max(b+c-a,0ll);i<=l;i++) {
        ll res = l - i;
        ll v = min(res, a+i-b-c);
        cur += (v+2)*(v+1)/2;
    }
    return cur;
}
int main() {
    scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&l);
    ll ans = 0;
    for(ll i=0;i<=l;i++) {
        ans += (i+2)*(i+1)/2;
    }
    ans -= solve(a,b,c);
    ans -= solve(b,a,c);
    ans -= solve(c,a,b);

    printf("%I64d\n",ans);
    return 0;
}


你可能感兴趣的:(排列组合,ACM-ICPC,容斥定理)