kuangbin专题23 二分 尺取 单调栈队列
对于位置问题似乎是可以考虑二分选择出合法的位置Queue Pie Median这三题都是这样子的
//想分解公式但是什么都没看出来,这个公式是用于判断单调性的
//主函数里面二分答案,check二分查找有多少个小于当前M的数
//对于相同的j,i越大结果越大
题意:给你一个n * n的矩阵,矩阵一点的值是i^2 + 100000 × i + j^2 - 100000 × j + i × j,问在整个矩阵中第m大的值是多少。
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<queue>
#include<stack>
#include<set>
#define endl "\n"
typedef long long ll;
using namespace std;
const int N=1e5+5;
int n,a[N],m;
bool check(int x){
int cnt=0;
//cout<<x<<endl;
for(int i=1;i<=n;i++){
cnt+=n-(lower_bound(a+1,a+1+n,a[i]+x)-a-1);
//cout<<a[i]+x<<endl;
}
//cout<<x<<' '<<cnt<<endl;
return cnt>m;
}
int main(){
while(~scanf("%d",&n)){
m=n*(n-1)/4;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+1+n);
int L=0,R=a[n]-a[1];
while(L+1<R){
int M=(L+R)>>1;
if(check(M))L=M;
else R=M;
}
printf("%d\n",L);
}
return 0;
}

和Matrix很像,排序后对于一个固定的 i ,越在后面的 j 差值一定是越大的,抓住这个性质,枚举 i 二分 j 即可
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<queue>
#include<stack>
#include<set>
#define endl "\n"
typedef long long ll;
using namespace std;
const int N=1e5+5;
int n,a[N],m;
bool check(int x){
int cnt=0;
//cout<<x<<endl;
for(int i=1;i<=n;i++){
cnt+=n-(lower_bound(a+1,a+1+n,a[i]+x)-a-1);
//cout<<a[i]+x<<endl;
}
//cout<<x<<' '<<cnt<<endl;
return cnt>m;
}
int main(){
while(~scanf("%d",&n)){
m=n*(n-1)/4;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+1+n);
int L=0,R=a[n]-a[1];
while(L+1<R){
int M=(L+R)>>1;
if(check(M))L=M;
else R=M;
}
printf("%d\n",L);
}
return 0;
}
//------------------------------------------------
浮点数二分

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<queue>
#include<stack>
#include<set>
#include<iomanip>
#define endl "\n"
#define PI acos((double)-1)
typedef long long ll;
using namespace std;
const int N=1e4+5;
const double eps=1e-8;
int n,m,a[N];
bool check(double x){
int cnt=0;
for(int i=1;i<=n;i++){
cnt+=(int)(a[i]/x);
}
return cnt>=m;
}
int main(){
//ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T=1;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
double sum=0;m++;
//不能在这就计算整个派的体积(二分体积除以Π,最后输出答案再乘上Π,会更精确,防止因为精度问题wa)
for(int i=1;i<=n;i++){scanf("%d",&a[i]);a[i]*=a[i];sum+=a[i];}
double L=0,R=sum/m;
while(R-L>eps){
double M=(L+R)/2;
if(check(M))L=M;
else R=M;
}
printf("%.6f\n",L*PI);
}
return 0;
}
二分+树状数组

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<queue>
#include<stack>
#include<set>
#include<iomanip>
#define endl "\n"
#define int long long
#define PI acos((double)-1)
typedef long long ll;
using namespace std;
const int N=2e5+5;
struct Node{
int h,num;
}a[N];
bool cmp(Node x,Node y){
return x.h<y.h;
}
int Tree[N],ans[N],n;
void modify(int x,int k) {
for (; x < N; x += x & (-x)) {
Tree[x] += k;
}
}
int qurey(int x) {
int ans = 0;
for (; x; x -= x & (-x)) {
ans += Tree[x];
}
return ans;
}
int find(int x){
//树状数组L不能是0,写法也要变成L<R而不是L+1<R
int L=1,R=n;
while(L<R){
int M=(L+R)>>1;
if(qurey(M)<x)L=M+1;
else R=M;
}
return L;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T=1;cin>>T;
int now=0;
while(T--){
now++;
memset(Tree,0,sizeof(Tree));
cin>>n;
for(int i=1;i<=n;i++){
//每个可能有人的位置加一
modify(i,1);
cin>>a[i].h>>a[i].num;
}
int flag=0;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
if(n-i-a[i].num<0){
flag=1;
break;
}
int x=min(n-i-a[i].num,a[i].num)+1;
int pos=find(x);
//因为高度是sort后的,后面的高度只会越来越大,当前位置是不可能比后面的位置高的
//故将这个点变回0
modify(pos,-1);
ans[pos]=a[i].h;
//cout<<pos<<' '<<a[i].h<<endl;
}
cout<<"Case #"<<now<<": ";
if(flag)cout<<"impossible\n";
else {
for(int i=1;i<=n;i++)cout<<ans[i]<<" \n"[i==n];
//cout<<endl;
}
}
return 0;
}
浙公网安备 33010602011771号