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; }