我终于要补题了,因为我发现稳定过三题还是难.
以后打算把Div.2的千人题都尽力补了.
A
交了一次WA了后发现漏读一个只能从i到i+1的条件,最后写了个模拟,挺费事的,正解是拿前缀和不停地和公式比较.
B
货舱选址问题,熟悉,一发入魂.
C1,C2
止步于此,太弱了.
一开始自作聪明写了个假的二分,完全没有意识到复杂度不对劲,不停地WA.
直接说C2的算法(也能通过C1),先在1~n中找到第二大的数的位置pos,然后看看1~pos中第二大是不是还是pos,如果是说明最大在1~pos-1,否则在pos+1~n,那么分类讨论,二分求解这两个范围.
边界条件就是pos==1,2,n-1,n,而只需要ask()里判断一下就可以了,感觉不是很容易想到.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n; int ask(int x, int y) { if (x >= y) return -1; cout << "? " << x << ' ' << y << endl; cout.flush(); int ret; cin >> ret; return ret; } int main() { cin >> n; int pos = ask(1, n); if(ask(1, pos) == pos){ // on the left int l = 1, r = pos - 1; while(l < r){ int mid = l + r + 1 >> 1; if(ask(mid, pos) == pos) l = mid; else r = mid - 1; } cout << "! " << l << endl; }else{ int l = pos + 1, r = n; while(l < r){ int mid = l + r >> 1; if(ask(pos, mid) == pos) r = mid; else l = mid + 1; } cout << "! " << l << endl; } return 0; }
D
对于一个有序序列中的一个数,想要知道它是不是中位数,只需要关心不小于他的数和比他小的数的数量.
如果想要一个有序序列的中位数是否不小于于x,只需要看不小于x的数是否达到序列长度的一半.
后者是一个以x为自变量的具有单调性的问题,使用二分答案求解.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, k, s[200010]; int pre[200010]; bool check(int x){ for(int i = 1; i <= n; i++){ if(s[i] >= x) pre[i] = 1; else pre[i] = -1; pre[i] += pre[i - 1]; } int small = 0; for(int i = k; i <= n; i++){ small = min(small, pre[i - k]); if(pre[i] - small > 0) return true; } return false; } int main() { cin >> n >> k; for(int i = 1; i <= n; i++) cin >> s[i]; int l = 1, r = n; while(l < r){ int mid = l + r + 1 >> 1; if(check(mid)) l = mid; else r = mid - 1; } cout << l << endl; return 0; }
最后发现B是中位数,C是二分,D是中位数与二分.