2015-06-10 17:06:06

传送门

总结:挺久以前开的 vp,正赛没做。。

 

A题:数学、二分

  题意:给出一个序列 A,A+B,A+2B,...,A+nB

  每次询问会给出 l,t,m,意思是从序列中选取一段 A+(l-1)B,...,A + (r-1)B,每次你可以选择不同的最多 m 个数使他们都减1,最多执行 t 次,问最大的 r 值为多少。

  思路:比较明显的二分答案,但是检查的步骤呢?

  需要考虑两点:(1)序列的最大值不能超过 t,这个比较显然。

         (2)序列的和不能超过 m×t

  神奇的是只用考虑以上两点即可。

  简证:(1)如果序列个数小于等于m,显然正确。

  (2)序列个数大于 m,每次取 m 个最大的数让他们减1,最后肯定会剩一堆很小的数,由于 t 大于等于最大值,所以最后必定能减完。

  官方的Tutorial给的是一种类似数学归纳法

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)

typedef long long ll;
typedef pair<int,int> pii;
const int INF = (1 << 30) - 1;

int A,B,n;
int l,t,m;
ll st;

bool Check(ll r){
    //l -> r
    ll R = (ll)A + (ll)(r - 1) * B;
    if(t < R) return false;
    if((st + R) * (r - l + 1) / 2 > (ll)t * m) return false;
    return true;
}

int main(){
    scanf("%d%d%d",&A,&B,&n);
    REP(i,n){
        scanf("%d%d%d",&l,&t,&m);
        st = (ll)A + (ll)(l - 1) * B;
        ll sum = (ll)t * m;
        ll top = l + sum / A;
        ll L = l,R = top;
        while(L < R){
            ll mid = getmid(L,R);
            if(Check(mid)) L = mid + 1;
            else R = mid;
        }
        if(L <= l) printf("-1\n");
        else printf("%I64d\n",L - 1);
    }
    return 0;
}
View Code

相关文章: