队内训练5 二分答案+思维
题目链接link.
第四周训练由于一直在补材料以及过于颓废所以鸽了,这周一定(
题意:给你一个n以及价值为 [ 0 , n − 1 ] [0,n-1] [0,n−1] 的 n 堆石子的个数,每次可以选取任意个石子进行mex合成操作,合成后的石子的价值即为这堆石子价值的 mex ,操作若干次后只剩下一个石子,问这最后剩下的一个石子的最大值是多少?
考虑二分答案,从大到小遍历,多余的往01堆里扔,维护前缀和就好。
(好困,突然感觉写题解好烦,不想细说了就这吧)
自认为这一篇码风很好
AC代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
int n;
int c[100070];
int sum=0;
int ok(int x)
{
if(x<=2) return 1;
if(x > 1e5+60) return 0;
int cnt=0;
int qzh=1;
for(int i=n;i>=x;i--)
cnt+=c[i];
for(int i=x-1;i>=2;i--)
{
int haha;
if(i<=n-1) haha=c[i]-qzh;
else haha=0-qzh;
if(haha>=0) cnt=cnt+haha;
else qzh=qzh-haha;
if(qzh>1e14+10) return 0;
}
cnt=cnt+c[0]+c[1];
qzh=qzh*2;
if(cnt>=qzh) return 1;
else return 0;
}
signed main( )
{
scanf("%lld",&n);
for(int i=0;i<=n-1;i++)
scanf("%lld",&c[i]),sum+=c[i];
if(sum==1)
{
int one=1;
for(int i=0;i<=n-1;i++)
if(c[i]) { printf("%lld\n",max(i,one)); break; }
}
else if(sum==2) { printf("2\n"); }
else
{
int l=1,r=(1e5+60)*2;
while(1)
{
if(r-l<=1) break;
int mid=(l+r)/2;
if(ok(mid)!=0) l=mid;
else r=mid;
}
if( ok(l+1) != 0 ) printf("%lld\n",l+1);
else if(ok(l)!=0) printf("%lld\n",l);
else if(ok(l-1)!=0) printf("%lld\n",l-1);
}
return 0;
}