链接
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;
}