NOIP2011提高组Day 2

版权声明:吸吸 https://blog.csdn.net/walk_dog/article/details/80958589

T1


OJ传送门
洛谷传送门
这道题当时快速幂的模板写错了,很震惊居然还能拿到60分
然后就是广为人知的数学选修2-3的二项式定理,如果我没记错的话,是这样的:
那这道题最重要的一行代码就是:int ans=power(a,n)*power(b,m)%mod*C(k,m)%mod;

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;
const ll MOD = 10007;
ll rec[1010][1010];
ll a,b,k,n,m;

ll C(ll n,ll r)
{
    if(n==r || r==0)    return rec[n][r]=1;
    if(rec[n][r])    return rec[n][r];
    return rec[n][r] = (C(n-1,r)+C(n-1,r-1)) % MOD;
}
ll power(ll x, ll k)
{
    ll ans = 1;
    while(k)
    {
        if(k & 1)    ans = ans * x % MOD;
        x = x * x % MOD;
        k >>= 1;
    }
    return ans;
}
int main()
{
    scanf("%lld%lld%lld%lld%lld", &a, &b, &k, &n, &m);
    ll ans = power(a, n) * power(b, m) % MOD * C(k, m) % MOD;
    printf("%lld",ans);
    return 0;
}

T2



OJ传送门
这道题用二分和前缀和的做法,在最大质量和最小质量中间二分,但为了避免忘记特判特殊端点,二分的真正范围是max_weight+2与min_weight-1之间。具体思想是,W越小,Y越大;W越大,Y越小。当Y>S时,我们要增大W的值,使abs(Y-S)变小,反之同理。
维护前缀和就维护pre_v[i]维护到第i个矿石时满足条件的所有矿石的价值,pre_cnt[i]维护的是前i个矿石符合条件的矿石数。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long ll;
const int MAXN = 2e5+5;
ll n, m, S, l_i, r_i;
ll max_weight = -1, min_weight = 99999999;
ll Y, sum, answer;
ll l[MAXN], r[MAXN];
ll w[MAXN], v[MAXN], pre_v[MAXN], pre_cnt[MAXN];

bool check(ll W)
{
    memset(pre_v, 0, sizeof(pre_v));
    memset(pre_cnt, 0, sizeof(pre_cnt));
    Y = 0, sum = 0;
    for(ll i = 1; i <= n; i++)
    {
        if(w[i] >= W)
        {
            pre_v[i] = pre_v[i-1] + v[i];
            pre_cnt[i] = pre_cnt[i-1] + 1;
        } else {
            pre_v[i] = pre_v[i-1];
            pre_cnt[i] = pre_cnt[i-1];
        }
    }
    for(ll i = 1; i <= m; i++)
        Y += (pre_cnt[r[i]] - pre_cnt[l[i]-1]) * (pre_v[r[i]] - pre_v[l[i]-1]);
    sum = llabs(Y - S);
    if(Y > S) return true;
    else return false;
}

int main()
{
//  freopen("qc.in","r",stdin);
//  freopen("qc.out","w",stdout);
    scanf("%lld%lld%lld", &n, &m, &S);
    for(ll i = 1; i <= n; i++)
    {
        scanf("%lld%lld", &w[i], &v[i]);
        max_weight = max(max_weight, w[i]);
        min_weight = min(min_weight, w[i]);
    }
    for(ll i = 1; i <= m; i++)
        scanf("%lld%lld", &l[i], &r[i]);
    ll left = min_weight-1, right = max_weight+2;
    ll answer = 99999999999;
    while(left<=right)
    {
        ll mid = (left+right) >> 1;
        if(check(mid)) left = mid+1;
        else right = mid-1;
        if(sum < answer) answer = sum;
    }
    printf("%lld", answer);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/walk_dog/article/details/80958589
今日推荐