TopCoder Open 2016 R2B

300

  这题让计算三条边分别不超过 a b c 的三角形有多少个,数据范围1e9,比赛时写了个 O(1e9) 的,悲惨爆零。
  正确的做法是利用容斥原理 O(1) 。如果三条边可以是不超过 a b c 的任何数,答案就是abc,但是肯定有些情况是非法的,只需要考虑两边长度大于等于第三边就行了,把非法的减去就是答案。具体的计算过程还是需要细心推一下的,只需要很基本的数学知识就能够推出来(但是我补题的时候还是调了很久)。
  总结一下,当计算某些值比较困难(合法情况),但是反过来比较简单的时候(总数和非法情况),就可以考虑容斥。

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const ll mod = 1000000007;

// *inv%mod 相当于/6 
const ll inv = 166666668;

const ll LARGE = 3e18;

ll calc(ll n){
    ll res = n*(n+1);
    res %= mod;
    res *= (2*n+1);
    res %= mod;
    res *= inv;
    res %= mod;
    return res;
}

ll solve(ll a,ll b,ll c){
    ll res = 0;

    ll MIN = min(a,b);
    ll MAX = max(a,b);
    ll tmp = 0;

    // part 1
    if(c>1){
        ll end = min(MIN,c-1);
        tmp += end*(1+end)/2;
        tmp %= mod;
        tmp *= c;
        tmp -= calc(end);
        tmp %= mod;
        tmp += mod;
        tmp %= mod;
        res += tmp;
        //cout<<"part 1 = "<<tmp<<endl; 
    }
    // part 1 end

    // part 2
    tmp = 0;
    ll l = MIN+2;
    ll r = min(c,MAX+1);
    ll cnt = r-l+1;
    if(cnt>0){
        tmp += ((c-l+1)+(c-r+1))*cnt/2;
        tmp %= mod;
        tmp *= MIN;
        tmp %= mod;
        res += tmp;
        //cout<<"part 2 = "<<tmp<<endl;
    }
    // part 2 end

    // part 3
    if(c>MAX+1){
        tmp = 0;
        ll cnt = min(MIN,c-MAX)-1;

        tmp += (MIN-1+(MIN-cnt))*cnt/2;
        tmp %= mod;
        tmp *= (c-MAX);
        tmp %= mod;
        tmp += calc(cnt);
        tmp %= mod;
        tmp -= MIN*((1+cnt)*cnt/2%mod);
        tmp %= mod;
        if(tmp<0){
            tmp += mod;
        }
        res += tmp;
        //cout<<"part 3 = "<<tmp<<endl;
    }
    // part 3 end

    res %= mod;
    return res;
}


class TriangleTriples{
public:
    int count(ll A, ll B, ll C){
        ll res = A*B;
        res %= mod;
        res *= C;
        res -= (solve(A,B,C) + solve(C,A,B) + solve(B,C,A))%mod;
        res %= mod;
        res += mod;
        res %= mod;
        return res;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值