Codeforces Round #703(div2 A~D ) 补题

链接

http://codeforces.com/contest/1486

A. Shifting Stacks

题意:
有n堆石头 , 可以把第n堆的任意石头放到第n+1堆去 。 问能否让这n堆石头的石子数严格单调递增。
思路:
严格单增最极端的情况就是 0,1,2,3,4,…
那么只需判断sum[i] < i*(i-1)/2 即可 。

B. Eastern Exhibition

题意:
现在有n座房子 , 它们的坐标分别是(xi , yi) , xi,yi<=109 , 且为整数 。
要求找到一个适合建展览馆的位置 , 使得该位置到所有房子的曼哈顿距离之和最小
问满足条件的该位置有几个 。
思路:
点阵问题, 从未见过的话会有点难想。
如果是奇数座房子 , 那么ans只能是1 , 因为将该点移动至相邻处不可能使奇数个位置的距离和保持不变 。
如果是偶数 , 那么ans就是最中间的小矩形面积 , 即 |xmid+1 - xmid| * |ymid+1 - ymid| .
代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll n;
    const int maxn = 3000;
    struct node{
    	ll x;
    	ll y;
    }a[maxn];
    bool cmpx(node A,node B){
    	return A.x<B.x;
    }
    bool cmpy(node A,node B){
    	return A.y<B.y;
    }
    int main(){
    	int t;
    	cin>>t;
    	while(t--){
    		ll n;
    		cin>>n;
    		ll x,y;
    		for(int i=0;i<n;i++){
    			cin>>x>>y;
    			a[i].x = x;
    			a[i].y = y;
    		}
    		if(n%2==1)
    		cout<<1<<endl;
    		else{
    			sort(a,a+n,cmpx);
    			ll now = n/2-1;
    			ll ans1 = a[now+1].x - a[now].x +1;
    			//printf("ans1==%d\n",ans1);
    			sort(a,a+n,cmpy);
    			now = n/2-1;
    			ll ans2 = a[now+1].y - a[now].y +1;
    			//printf("ans2==%d\n",ans2);
    			cout<<ans1*ans2<<endl;
    		}
    	}
    }

C1 / C2. Guessing the Greatest(交互+二分)

题意:
给一个长度为n的序列a , 每次询问 ?[l,r] 可以得到区间 [l,r] 的第二大元素的位置 , 要求找到这个序列最大元素的位置 。 (n <=1*105 )
其中easy version 允许进行最多40次询问 , 而hard version 允许最多20次 。
思路:
明天更

代码:

easy version:

    #include<bits/stdc++.h>
    using namespace std;
    int get(int l,int r){
    	cout<<"? "<<l<<" "<<r<<endl;
    	cout.flush();
    	int ans = 0;
    	cin>>ans;
    	return ans;
    } 
    void print(int x){
    	cout<<"! "<<x<<endl;
    	cout.flush();
    	return;
    }
    int main(){
    	int n;
    	cin>>n;
    	int left = 1;
    	int right = n;
    	int se = -1;
    	while(left+1<right){
    		se = get(left,right);
    		int mid = (left+right) / 2;
    		if(se<mid){
    			if(get(left,mid)==se){
    				right = mid;
    			}
    			else{
    				left = mid;
    			}
    		}
    		else{
    			if(get(mid,right)==se){
    				left = mid;
    			}
    			else{
    				right = mid;
    			}
    		}
    	}
    	se = get(left,right);
    	if(left==se)
    	print(right);
    	else
    	print(left);
    	return 0;
    } 

hard version:

    #include<bits/stdc++.h>
    using namespace std;
    int get(int l,int r){
    	if(l==r)
    	return -10;
    	cout<<"? "<<l<<" "<<r<<endl;
    	cout.flush();
    	int ans = 0;
    	cin>>ans;
    	return ans;
    } 
    void print(int x){
    	cout<<"! "<<x<<endl;
    	cout.flush();
    	return;
    }
    int main(){
    	int n;
    	cin>>n;
    	int left = 1;
    	int right = n;
    	int se = get(left,right);
    	if(get(se,right)==se){
    		left = se;
    		int mid  = -1;
    		while(left<=right){
    			mid = (left+right) / 2;
    			if(get(se,mid)==se){
    				right = mid - 1;
    			}
    			else{
    				left = mid + 1;
    			}
    		}
    		print(left);
    	}
    	else{
    		int mid  = -1;
    		right = se;
    		while(left<=right){
    			mid = (left+right) / 2;
    			if(get(mid,se)==se){
    				left = mid+1;
    			}
    			else{
    				right = mid-1;
    			}
    		}
    		print(right);
    	} 
    	
    	return 0;
    } 

D. Max Median(二分答案+前缀和)

在这里插入图片描述

题意:
给一个长度为n的序列a , 要求在长度不小于k的子段中找出最大的中位数 。 n,k<=2*105
思路:
二分答案 , 判断当前的mid是否可以是中位数。(即是否存在子段 , 使其中位数大于等于mid)
要O(n) 判断当前的mid是否可以是中位数 , 需要用到前缀和。
对于大于等于mid的元素 , 将它标记为1 , 小于mid的元素,标记为-1 。 这样,当前缀和sum[i] >0 时就代表 [a1 , ai ] 的中位数大于等于mid 。
那么,要判断是否存在这样的子段,每次还需要预处理一个 sum_min[i] , 来表示从1~i 最小的前缀和 , 即 sum[i] - sum_min[i-k] 。

代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    const ll maxn = 2e5+5;
    ll a[maxn];
    ll b[maxn];
    ll sum[maxn];
    ll sum_min[maxn];
    ll n,k;
    bool check(ll now){
    	for(int i=1;i<=n;i++){
    		if(a[i]>=now)
    		b[i] = 1;
    		else
    		b[i] = -1;
    		sum[i] = sum[i-1] + b[i];
    		
    		sum_min[i] = min(sum[i],sum_min[i-1]);
    	}
    	for(int i=k;i<=n;i++){
    		if(sum[i]-sum_min[i-k]>0)
    		return 1;
    	}
    	return 0;
    }
    int main(){
    	cin>>n>>k;
    	for(int i=1;i<=n;i++)
    	scanf("%lld",&a[i]);
    	ll left = 1,right = n;
    	ll ans = 0;
    	while(left<=right){
    		ll mid = (left+right)/2;
    		if(check(mid)==1)
    		ans = mid , left = mid+1;
    		else
    		right = mid-1;
    	}
    	cout<<ans<<endl;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值