372A - Counting Kangaroos is Fun
1698D - Fixed Point Guessing 二分
不知道该咋写check函数,其实如果二分的这个区间内有偶数个是属于这个区间的那么就说明可能是两两交换的,所以不会存在于这个区间,如果有奇数个说明答案是一定在这个区间的,根据这个写出check函数就可以
D. Fixed Point Guessing——交互题+二分_星染*的博客-CSDN博客
const ll mod=998244353;
const double inf=1e18;
const double eps=1e-8;
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll getinv(ll a){return qpow(a,mod-2);}
ll lcm(ll a,ll b){
return a*b/__gcd(a,b);
}
ll n,a[100005];
bool check(ll x,ll y){
ll res=0;
for(int i=x;i<=y;i++)
if(a[i]>=x&&a[i]<=y) res++;
return (res&1);
}
int main(){
ll t;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
ll l=1,r=n,ans=0;
while(l<=r){
ll mid=l+r>>1;
printf("? %lld %lld\n",l,mid);
fflush(stdout);
for(int i=l;i<=mid;i++) scanf("%lld",&a[i]);
if(check(l,mid)) r=mid-1,ans=mid;
else l=mid+1;
}
printf("! %lld\n",ans);
fflush(stdout);
}
return 0;
}
C - Necklace 二分
二分答案,难在check函数,先选中a[1],第一段区间是[a[1]-mid+1,a[1]],然后处理a[i],如果a[i]和a[i-1]之间的数不超过mid,那么a[i]就可以去右边扩展,不然a[i]之前的整体就要右移,offset表示第一段右移了多少距离,emo表示当前的整体可以向右移动多少距离,lst表示上一个区间的右端点,如果要右移的距离不超过emo就可以将当前的整体右移,否则return 0,如果到了最后两个区间还是没连接起来也是return 0;
2021CCPC广州 C. Necklace(二分/贪心/好题/详细题解) - 脂环 - 博客园 (cnblogs.com)
这个博客其实已经写得挺好了,但是更新emo的时候思路说的挺好,但到了代码上就很难理解了,不知道他怎么想的,我改了一下代码的位置便于好理解
ll n,m,a[1000006];
bool check(ll mid){
ll len=mid,offset=0,emo=mid-1,lst=a[1];
ll yj0=0,yj1=0;
for(int i=2;i<=m;i++){
if(a[i]-len<=lst){
emo=min(emo,a[i]-lst-1);
ll res=len-(a[i]-lst);
lst=a[i]+res;
if(i==m){
if(lst>n){
lst=lst%n;
lst=min(lst,a[1]-1);
yj1=1;
}
}
else{
lst=min(lst,a[i+1]-1);
}
}
else{
if(a[i]-lst+1>2*len) return 0;
ll need=a[i]-lst-len;
if(emo<need) return 0;
else offset+=need,emo-=need,lst=a[i];
}
}
ll lmst=0,rmst=0;
if(a[1]-len+1+offset>=1) lmst=a[1]-len+1+offset;
else lmst=n+a[1]-len+1+offset,yj0=1;
rmst=lst;
if(yj0&&yj1) return 1;
else if(yj0){
if(lmst<=rmst+1) return 1;
else return 0;
}
else if(yj1){
if(rmst+1>=lmst) return 1;
else return 0;
}
else{
if(lmst==1&&rmst==n) return 1;
else return 0;
}
}
int main(){
cin.tie(0);
ios::sync_with_stdio(false);
//scanf("%lld%lld",&n,&m);
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>a[i];
if(m==1){
cout<<n;
return 0;
}
ll l=1,r=n,ans=1e18;
while(l<=r){
ll mid=l+r>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans;
return 0;
}