2 y x : change the A[y] to x. there are at most change 10 times.
For each ask can you tell me the length of longest substring.
InputFOJ有奖月赛-2011年11月
题目大概意思说:有1..n个数,一共有M个操作,有两种操作,1 X,问你连续不小于X的数有多少个, 2 X Y将第X个数的值改为 Y
第一感觉这题就是个数据结构题,注意到题目中说第二个修个操作最多只有10次。根据经验每做一次修改跟定要对这个数据结构重新维护一遍
一开始顺着想的时候,对现在1..n个位置上的所有数从小到大排个序,然后对当前要查询的值全部读入(至第一个修改操作),并且从小到大排个序。
假如现在最小的数比当前查询的最小值要小的话,那么就把这个元素弹出,它所在的线段就一分为二。
可能这样说有点抽象,我举个例子,现在有[1, 10] = {2,2,2,2,1,2,2,2,2,2}有10个数,现在查询的值最小的是2,那么这串数中当前最小的是处于第5个位置上的1,然后把这个1删掉,那么[1, 10]这条短短就会分裂成[1, 4] 和 [6, 10]两段..然后比较当前所剩元素的最小值是2, 大于等于现在要查询的最小值2,然后就从现有的两条线段[1, 4] 和 [6, 10]选一个长度最长的就是答案。这样问题就简单了,每出一个数就相当于将一条选段分成两段,当最小元素的值大于等于当前查询值的最小值时,当前所有线段的最长的长度就是答案。不过这么顺着做的话问题就卡在如何快速找到那条要分割的线段,并且快速找到最长的线段。我纠结了一下,估计写个平衡树+堆应该可以。但是我看了下通过的人数,发现还不少,于是我断定这题肯定有更简单的方法。
然后今晚突然灵光一闪,那我将一条线段分割成两端,我倒过来想,不就是将两条线段合并成一段么? 这不就是赤裸裸的并查集嘛~!!
突然恍然大悟...
第一步还是把两样东西排个序,然后从大的开始做,初始每个元素为一条长度为1的线段,假如现在最大的元素比要查询的最大的值要大于或等于的话,就看看这个元素的左边和右边的点是否已被访问,如果是,则把这个点合并到那条上段,用个size数组记录下每个线段所包含的元素的个数,(相当于并查集的加权法则了)..然后后面就是赤裸裸的并查集,很快写完 ,检查了下~交上去1Y了...
再次证明有事正向做的时候发觉很难做下去的时候不妨用用逆向思维去考虑,问题就会顿时变得简单多了~
PS:网上好像有大牛是用栈+二分过的...YM一下
附上代码
#include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstring> #include <algorithm> #include <functional> #include <set> #define Maxn 100100 #define Maxm 10010 #define mp make_pair #define fi first #define se second using namespace std; typedef pair<int,int> pii; int n, m, d[Maxn], ans[Maxn]; int op[Maxm][2], st, tot, max_len; pii c[Maxn], ask[Maxm]; int fa[Maxn], size[Maxn]; bool vis[Maxn]; bool cmp(const pii &a, const pii &b) { return a.fi < b.fi; } int find(int x) { if (fa[x] == x) return x; else return fa[x] = find(fa[x]); } void Union(int x, int y) { int r1 = find(x), r2 = find(y); if (r1 == r2) return; if (size[r1] < size[r2]) { size[r1] += size[r2]; max_len = max(max_len, size[r1]); fa[r2] = r1; } else { size[r2] += size[r1]; max_len = max(max_len, size[r2]); fa[r1] = r2; } return; } void calc(int en) { max_len = 0; for (int i=0; i<n; i++) c[i] = mp(d[i], i); sort(c, c+n, cmp); tot = 0; for (int i=st; i<en; i++) ask[tot++] = mp(op[i][0], i); sort(ask, ask+tot, cmp); for (int i=0; i<=n; i++) fa[i] = i, size[i] = 1, vis[i] = false; int p1 = n-1; for (int i=tot-1; i>=0; i--) { while (p1 >= 0 && c[p1].fi >= ask[i].fi) { vis[c[p1].se] = true; if (max_len == 0) max_len = 1; if (c[p1].se+1 < n && vis[c[p1].se+1]) Union(c[p1].se, c[p1].se+1); if (c[p1].se-1 >=0 && vis[c[p1].se-1]) Union(c[p1].se, c[p1].se-1); p1--; } ans[ask[i].se] = max_len; } } int main() { int cmd; while (~scanf("%d%d", &n, &m)) { st = 0; memset(ans, -1, sizeof(ans)); for (int i=0; i<n; i++) scanf("%d", &d[i]); for (int i=0; i<m; i++) { scanf("%d", &cmd); if (cmd == 1) scanf("%d", &op[i][0]); else { scanf("%d%d", &op[i][0], &op[i][1]); calc(i); d[op[i][0]-1] = op[i][1]; st = i+1; } } if (st < m) calc(m); for (int i=0; i<m; i++) if (ans[i] != -1) printf("%d\n", ans[i]); } return 0; }