A. Shifting Stacks
呜呜,比赛时A题就做了一小时
注意积木只能往后移动,可以这样理解,只要此时的积木总数大于能满足递增序列的积木总数的最小数即可。
最小积木数 0 1 2 3 4 5 6 7 8…
如果积木多出来了,只要将多余的积木放在最后就行。
因为积木只能向后移动,所以要遍历判断n个区间。
#include<bits/stdc++.h>
using namespace std;
int t, n;
typedef long long ll;
ll h[102];
int main(){
cin >> t;
while(t--){
cin >> n;
ll sum=0, sum1=0;
bool f=1;
for(int i=0; i<n; ++i){
cin >> h[i];
sum += i;
if(f){
if(sum1+h[i] >= sum){
sum1 += h[i];
}
else f=0;
}
}
if(f) cout << "yes" << endl;
else cout << "no" << endl;
}
}
B. Eastern Exhibition
先看下货舱选址的结论
求|x1−x2|+|y1−y2|的最小值,只要|x1−x2|和|y1−y2|都是最小即可
n为奇数时,一个点。
n为偶数时,算下乘积。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n;
ll x[1003], y[1003];
int main(){
cin >> t;
while(t--){
cin >> n;
for(int i=1; i<=n; ++i){
cin >> x[i];
cin >> y[i];
}
sort(x+1, x+n+1);
sort(y+1, y+n+1);
if(n%2 == 1) cout << '1' << endl;
else {
ll sum = (x[n/2+1]-x[n/2]+1) * (y[n/2+1]-y[n/2]+1);
cout << sum << endl;
}
}
}
C1. Guessing the Greatest (easy version)
比赛的时候一直没用二分做,呜呜我太菜了
附上TLE代码
最坏的情况假如每次查询到第二大的值就是下标就为r,则每次r=ans-1,相当于遍历n个元素,text3的时候就TLE啦。
#include <bits/stdc++.h>
using namespace std;
int ask(int l, int r)
{
if (l >= r) return -1;
cout << "? " << l << ' ' << r << endl;
int ans;
cin >> ans;
return ans;
}
int main()
{
int n;
cin >> n;
int l = 1, r = n;
while (l < r)
{
int ans = ask(l, r);
if (ask(l, ans) == ans) r = ans - 1;
else l = ans + 1;
}
cout << "! " << l << endl;
}
二分,查询次数2*logn。
先查询[l, r]的第二大值,如果在[l, mid]中,查询[l, mid]的第二大值,如果二者相等,说明最大值在[l, mid]区间中,r=mid,反之l=mid+1。
第二种情况,第二大值在[mid+1, r]中,查询此区间第二大值,如果相等,l=mid+1,反之r=mid。
#include <bits/stdc++.h>
using namespace std;
int n;
int ask(int l, int r)
{
if (l >= r) return -1;
cout << "? " << l << ' ' << r << endl;
int ans;
cin >> ans;
return ans;
}
int main()
{
cin >> n;
int l = 1, r = n;
while(l < r)
{
int mid = l + r >> 1;
int ans = ask(l, r);
if (ans <= mid)
{
if (ask(l, mid) == ans) r = mid;
else l = mid + 1;
}
else
{
if (ask(mid + 1, r) == ans) l = mid + 1;
else r = mid;
}
}
cout << "! " << l << endl;
}
C2. Guessing the Greatest (hard version)
也是二分,查询次数2+logn,先查询[l, r]的第二大值,然后查询[l, ans],如果相等r=ans-1,反之l=ans+1。然后采用C1的方法就行了。(要用到两个二分模板)
#include <bits/stdc++.h>
using namespace std;
int ask(int l, int r)
{
if (l >= r) return -1;
cout << "? " << l << ' ' << r << endl;
int ans;
cin >> ans;
return ans;
}
int main()
{
int n;
cin >> n;
int l = 1, r = n;
int ans = ask(l, r);
if (ask(l, ans) == ans)
{
r = ans - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if (ask(mid, ans) == ans) l = mid;
else r = mid - 1;
}
}
else
{
l = ans + 1;
while(l < r)
{
int mid = l + r >> 1;
if (ask(ans, mid) == ans) r = mid;
else l = mid + 1;
}
}
cout << "! " << l << endl;
}
D. Max Median
在n长度的区间中,找长度大于等于k的区间中,最大的中位数
我们都知道无论n为奇偶,大于等于中位数的数的个数(包括本身)大于小于中位数的个数。
好家伙,这居然是二分。无需在意原数组,对[l, r](数的取值范围)二分查找最大中位数,大于等于mid的为1,小于的为-1,b[]存他们的前辍和。
因为只要大于等于k长度即可,因此我们从k+1到n遍历,求出最大区间和,如果mx>0,中位数可能时当前数,也可能时大于当前数的数,那么l=mid,反之r=mid-1,二分查找出来的数是大于0中的mx的最小值,我们可以证明只有查找到的数是原数组里的数,才能满足mx最小(n为偶数时mx为2,n为奇数时mx为1)。
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N], b[N];
int main()
{
int n, k;
cin >> n >> k;
for (int i=1; i<=n; i ++ ) cin >> a[i];
int l = 1, r = n;
while (l < r)
{
int mid = l + r + 1 >> 1;
for (int i=1; i<=n; i ++ )
{
if (a[i] >= mid) b[i] = 1;
else b[i] = -1;
}
for (int i=1; i<=n; i ++ ) b[i] += b[i-1];
int mx = b[k];
int mn = 0;
for (int i=k+1; i<=n; i ++ )
{
mn = min(mn, b[i - k]);
mx = max(mx, b[i] - mn);
}
if (mx > 0) l = mid;
else r = mid - 1;
}
cout << l << endl;
}