Codeforces Round #703 (Div. 2)
B
思路:结论题,如果点是奇数,这个点只能为1
否则等于中间最短点之间的差值+1
#include<stdio.h> #include<math.h> #include<string.h> #include<ctype.h> #include<iostream> #include<algorithm> #include<vector> typedef long long ll; using namespace std; inline void solve(){ int len;cin>>len; vector<ll> a(len+3),b(len+3); for(int i=0;i<len;i++) cin>>a[i]>>b[i]; sort(a.begin(),a.begin()+len); sort(b.begin(),b.begin()+len); if(len%2==1){ cout<<"1\n";return; } int l=(len-1)/2,r=len/2; cout<<(a[r]-a[l]+1)*(b[r]-b[l]+1)<<endl; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0),std::cout.tie(0); int sum;cin>>sum; while(sum--){ solve(); } }
C:
思路:思路是二分加构造,首先讨论在整个范围内x的次大值,然后再查询在1到x这个范围内次小值是否成立,那么最大值在左边界,不是最大值就在右边界
#include<stdio.h> #include<math.h> #include<string.h> #include<ctype.h> #include<iostream> #include<algorithm> #include<vector> typedef long long ll; using namespace std; int check(int l,int r){ cout<<"? "<<l<<' '<<r<<"\n"; cout.flush(); int x;cin>>x; return x; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0),std::cout.tie(0); int n,ans;cin>>n; int x=check(1,n);//标记次大值 if(x==1||check(1,x)!=x){//答案在x右边 int l=x+1,r=n; while(l<=r){ int mid=(l+r)>>1,pp=check(x,mid); if(pp==x)ans=mid,r=mid-1;//说明在x+1到mid这个范围内,存在最大值,然后让边界往左边收缩(因为x在左是已知的 else l=mid+1; } } else{ int l=1,r=x-1; while(l<=r){ int mid=(l+r)>>1,pp=check(mid,x);//同理,由于x已经定位在右侧,所以边界往右边收缩 if(pp==x)ans=mid,r=mid-1; else l=mid+1; } } cout<<"! "<<ans<<"\n"; }
另一种写法
#include<stdio.h> #include<math.h> #include<string.h> #include<ctype.h> #include<iostream> #include<algorithm> #include<vector> typedef long long ll; using namespace std; int check(int l,int r){ cout<<"? "<<l<<' '<<r<<"\n"; cout.flush(); int x;cin>>x; return x; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0),std::cout.tie(0); int n,ans;cin>>n; int x=check(1,n); if(x!=1&&check(1,x)==x){//在x左方向 int l=1,r=x-1; while(l!=r){ int mid=l+r+1>>1; if(check(mid,x)==x)l=mid;//mid逐渐向x靠拢 else r=mid-1; } cout<<"! "<<l<<"\n"; } else{ int l=x+1,r=n; while(l!=r){ int mid=l+r>>1; if(check(x,mid)==x)r=mid; else l=mid+1; } cout<<"! "<<l<<"\n"; } }
(尽量画个图判断一下,补题的时候左右不分浪费了好多时间)
D:
这里有一个常用的思维trick:假设某个数x为一段连续子序列的中位数,用一个额外的数组存储每位值,<x的为-1,大于等于的为1,最后把这些数都加起来,大于等于0说明这段序列的中位数大于等于x
先上代码
#include<stdio.h> #include<math.h> #include<string.h> #include<ctype.h> #include<iostream> #include<algorithm> #include<vector> typedef long long ll; using namespace std; int len,k; int num[200009],pre[200009],prmin[200009]; bool check(int x){ for(int i=0;i<200009;i++) pre[i]=0,prmin[i]=0; for(int i=1;i<=len;i++){ pre[i]=pre[i-1];prmin[i]=prmin[i-1];//统计pre状态 if(num[i]>=x)pre[i]++; else pre[i]--; if(pre[i]<prmin[i]) prmin[i]=pre[i];//统计在i状态下前面所有pre的最小值 } for(int i=k;i<=len;i++)//数组长度必然从K开始 if(pre[i]-prmin[i-k]>0)//减少最少的次数 return 1; return 0; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0),std::cout.tie(0); cin>>len>>k; for(int i=1;i<=len;i++) cin>>num[i]; int l=1,r=len,ans; while(l<=r){ int mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } cout<<ans<<"\n"; }
看完那些题解之后我个人是比较疑惑单调性为什么存在
感谢这位大佬的题解(32条消息) CF1486D - Max Median-巧妙二分答案_塔子哥来了的博客-CSDN博客
首先题目有一个关键信息
联系我上面所说的结论,我们可以去二分中位数数值,
设最终二分的答案为x,<=x的数值check都成立,大于x的check都不成立,即证明单调性