(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦
题意:传送门
原题目描述在最下面。
对于一段序列ai
,每次操作后ai'
等于前缀和ai
。
问多少次操作后序列最大值大于aim。
之前在牛客上做了一道类似的简单题,牛客的题是输出k次变化后的矩阵。牛客简单在答案输出mod。本题不能取模,然后爆longlong了???不想用大整数,懒得敲java。没想到居然用long double水过了?
思路:
很明显具有单调性。考虑二分操作次数。
在纸上列举长度为4的数组,4次操作后每项的系数可以发现规律。
第m次操作后:k = m - 1, 系数为:C(k, k), C(k+1, k), C(k+2, k) … C(k+n-1, k)
坑点就在爆long long。用long double, 然后暴力水组合数就行了。
具体看代码。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 1e9;
const int N = 1000007;
LL n, aim, ar[N];
bool ok(LL m){
long double tmp = aim;
LL k = m - 1;
for(int i = 1; i <= n; ++i){
long double a = 1, b = k + i - 1, res = ar[n-i+1];
if(res==0)continue;
LL x = min(k, i - 1LL);
for(LL i = 1; i <= x; ++i){
res = res*b/a;//这里就是水组合数C(k+i-1, k)
b--;a++;
if(res>=tmp) return 1;//注意res肯定是单增的,所以可以提前特判
}
tmp -= res;
if(tmp<=0)return 1;
}
return 0;
}
int main(int argc, char const *argv[]){
while(~scanf("%lld%lld", &n, &aim)){
int flag = 0;
for(int i = 1; i <= n; ++i){
scanf("%lld", &ar[i]);
if(ar[i] >= aim) flag = 1;
}
if(flag){
printf("0\n");
continue;
}
LL l = 1, r = (LL)1e18 + 5, mid, ans = 1;
while(l <= r){
mid = (l + r)>>1;
if(ok(mid))r = mid - 1, ans = mid;
else l = mid + 1, ans = mid + 1;
}
printf("%lld\n", ans);
}
return 0;
}