Taotao Picks Apples
ps:这题的难点,寻找修改的位置之后有多少个苹果能被吃掉,比赛的时候想到的是讨论修改的位置,预处理每个位置的最大值和吃到苹果的数量,记最大值所在的位置为 id, 记修改的位置为 pos,分三种情况讨论,① pos < id,维护一个从 id ~ 1的递减序列,修改后在这个序列里二分查找。② pos == id,维护一个 id + 1 ~ n的递增序列,修改后二分查找。三 pos > id,随便做。是不是感觉我说得很有道理~~~~,然而我并没写出来。实际上,这题离线后就好做了,从后往前维护一个单调栈,当前的元素修改了,就在单调栈中二分查找,O(mlog(n))。讲真,原来询问问题只有两种:强制在线和没要求……为啥我现在菜知道,orz。离线后一般要对询问排序,要不然离线没意义。(所以莫队的思想是很有用的),400ms哟
// 膜强无敌的老师: https://paste.ubuntu.com/p/jFChknqmCD/
inline void upd(int &x, int y) { x < y && (x = y); } const int N = 100005; int n, m; int a[N], cnt[N], Max[N], ans[N]; vector<int> v; vector<P> q[N]; void Inite() { v.clear(); cnt[0] = Max[0] = 0; for (int i = 1; i <= n; ++i) { cnt[i] = cnt[i - 1]; Max[i] = Max[i - 1]; if (a[i] > Max[i - 1]) cnt[i]++, Max[i] = a[i]; } } int main() { //freopen("D:\\1.in", "r", stdin); //freopen("D:\\1.txt", "w", stdout); BEGIN() { sc(n), sc(m); for (int i = 1; i <= n; ++i) sc(a[i]), q[i].clear(); Inite(); for (int i = 1; i <= m; ++i) { int pos, x; sc(pos), sc(x); q[pos].pb(P(x, i)); } for (int i = n; i >= 1; --i) { for (auto &p : q[i]) { int pos, x; tie(x, pos) = p; int res = cnt[i - 1] + (x > Max[i - 1]); upd(x, Max[i - 1]); res += v.rend() - upper_bound(v.rbegin(), v.rend(), x); ans[pos] = res; } while(v.size() && v.back() <= a[i]) v.pp(); v.pb(a[i]); } for (int i = 1; i <= m; ++i) pr(ans[i]); } return 0; }
补充:线段树也可以做的,主席树也可以。因为对于每个点我们需要向后找最近的大于它的一个值。假设修改的位置为pos ,树的每个区间存的是该区间的最大值,那么我们就可以去寻找(pos + 1, n)这个区间比 a[pos] 大的位置在哪。具体看代码~,言而总之,总而言之,我们的目的是预处理出 从 i 这个位置向后递增的序列长度。因为实现不太优秀的原因,跑了700多ms
inline void upd(int &x, int y) { x < y && (x = y); }
/* 比较扯的是 N = 100005 会RE,OMG,就真的不能一发过吗? */ const int N = 200005; int n, m, tot, mPos; int a[4 * N], b[N], cnt[N], Max[N], d[N]; map<int, int> id; void Inite() { mem(d, 0); cnt[0] = Max[0] = 0; for (int i = 1; i <= n; ++i) { cnt[i] = cnt[i - 1]; Max[i] = Max[i - 1]; if (b[i] > Max[i - 1]) cnt[i]++, Max[i] = b[i]; } } inline void Pushup(int root) { a[root] = max(a[lson], a[rson]); } void Build(int l, int r, int root) { if (l == r) { sc(a[root]); id[root] = ++tot; b[tot] = a[root]; return; } int mid = (l + r) >> 1; Build(l, mid, lson); Build(mid + 1, r, rson); Pushup(root); }
/*
写挂了的查询!① mPos可能存在,但if过后直接退出而查不到那个位置去,② 当找到mPos后可能在其它节点再次被更新,而且会T,因为查询的代价可能不再是O(log(n))
void Query(int l, int r, int root, int L, int R, int x) {
if (l > R || r < L || L > R) return;
if (L <= l && r <= R && l == r) {
mPos = id[root];
return;
}
int mid = (l + r) >> 1;
if (a[lson] > x) Query(l, mid, lson, L, R, x);
else if (a[rson] > x) Query(mid + 1, r, rson, L, R, x);
}
*/ void Query(int l, int r, int root, int L, int R, int x) { if (l > R || r < L || L > R || mPos != -1) return; if (L <= l && r <= R && l == r) { mPos = id[root]; return; } int mid = (l + r) >> 1; if (a[lson] > x) Query(l, mid, lson, L, R, x); if (a[rson] > x) Query(mid + 1, r, rson, L, R, x); } int main() { //freopen("D:\\1.in", "r", stdin); //freopen("D:\\1.txt", "w", stdout); BEGIN() { sc(n), sc(m); tot = 0; Build(1, n, 1); Inite(); for (int i = n; i; --i) { mPos = -1; Query(1, n, 1, i + 1, n, b[i]); if (mPos == -1) d[i] = 1; else d[i] = d[mPos] + 1; } //for (int i = 1; i <= n; ++i) cout << i << " " << d[i] << endl; for (int i = 1; i <= m; ++i) { int pos, x; sc(pos), sc(x); int res = cnt[pos - 1] + (x > Max[pos - 1]); upd(x, Max[pos - 1]); mPos = -1; Query(1, n, 1, pos + 1, n, x); if (mPos != -1) res += d[mPos]; pr(res); } } return 0; }
又ps:有心情的话我会补充主席树的代码,以及dp和倍增的代码,比较难求的东西只有一个,实现的方式只是手段。