JZOJ4708. 【NOIP2016提高A组模拟8.20】奇洛金卡达 倒着做的思想+并查集维护
2016-10-18 18:33
543 查看
题目大意
给定一个长度小于等于k的字符串和q,表示现在有q个操作,每个操作给定两个参数li,ri表示把现在的字符串第li到ri把其中编号为奇数的按顺序写下来,再在后面把编号为偶数的按顺序写下来,最后把写出的新字符串加在区间后面。一下为两个例子:
1、s1s2s3s4s5s6s7,li=3ri=5。操作后变成:s1s2s3s4s5s3s5s4s6s7
2、s1s2s3s4s5s6s7,li=4ri=7。操作后变成:s1s2s3s4s5s6s7s5s7s4s6
给定每组li,ri要求输出操作完的字符串的前k位是什么。
q≤5000
k≤3000000
li,ri≤109
解题思路
首先一个显然的性质,对于当前的字符串,在第k个·字符后面的肯定是没有用的。考虑题目给定的操作,由于字符串是动态变换的,所以很难维护字符串的信息。那么我们考虑对于操作,从后往前做,那么就能保证每确定一个位置,这个位置上的字符就不会变。照着这个思路我们只需每次找出字符串的第li个位置和第li+ri个位置,分别表示当前需要复制区间的其实位置以及复制后区间的起始位置。我们只需把后面的区间上的位置打上与前面某个位置相等的标记。然后由于这个区间已经确定下来,所以把这段区间标记为“删除”,并且在我们下次找区间第li个位置时就不能算上“删除”的位置。
现在就又遇到了两个问题,以一个是怎么找到第li个未被打上“删除”的位置,以及上某个位置下一个未被打上“删除”的位置。我们观察一下数据范围,我们发现只有5000个修改操作,并且一个操作肯定是连续的一段区间(就算不连续,中间的位置也一定是被打上删除标记的)。那么我们可以用并查集把相邻的“删除”位置并到一起,并记录下它覆盖的区间。那么找位置时可用的就是两个被“删除”的区间有有多少个位置,只需O(n)扫一遍就可以知道位置。对于第二个问题也一样,由于我们把相邻的“删除”的区间并在了一起,所以加入下一个位置是被“删除”了的,就只需跳到这个区间右边界+1就可以了。
最后输出时扫一遍就可以了。
程序
//YxuanwKeith #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int MAXN = 1e5 + 5, MAXM = 5e3 + 5, MAXK = 3e6 + 5; struct Node { int l, r; Node(int a, int b) {l = a, r = b;} Node() {} }; bool flag[MAXK]; Node D[MAXM], Coor[MAXK]; char S[MAXK], T[MAXK]; int n, k, len, top, l[MAXM], r[MAXM], ord[MAXK], lst[MAXK], deep[MAXK]; int Get(int s) { int get = 0; for (int j = 2; j <= top; j ++) { if (get + D[j].l - 1 - D[j - 1].r >= s) { return D[j - 1].r + s - get; break; } else get += D[j].l - 1 - D[j - 1].r; } return 0; } int Find(int x) { if (ord[x] == x) return x; ord[x] = Find(ord[x]); return ord[x]; } void Merge(int x, int y) { int fx = Find(x), fy = Find(y); if (fx == fy) return; if (deep[fx] > deep[fy]) swap(fx, fy); ord[fx] = fy; Coor[fy] = Node(min(Coor[fx].l, Coor[fy].l), max(Coor[fx].r, Coor[fy].r)); if (deep[fx] == deep[fy]) deep[fy] ++; } void Maketag(int p) { flag[p] = 1; if (flag[p - 1]) Merge(p, p - 1); if (flag[p + 1]) Merge(p, p + 1); } int Next(int s) { int p = s + 1; while (p <= k && flag[p]) p = Coor[Find(p)].r + 1; if (p > k || flag[p]) return 0; return p; } int main() { scanf("%s", S + 1); len = strlen(S + 1); scanf("%d%d", &k, &n); for (int i = 1; i <= n; i ++) scanf("%d%d", &l[i], &r[i]); D[1] = Node(0, 0), D[2] = Node(k + 1, k + 1); top = 2; for (int i = 1; i <= k; i ++) ord[i] = lst[i] = i, Coor[i] = Node(i, i); for (int i = n; i; i --) { int s1 = Get(l[i]), s2 = Get(r[i] + 1), len = r[i] - l[i] + 1; if (s2 == 0) continue; int cnt = 1, p = ((l[i] & 1) || len == 1) ? s1 : Next(s1), t = s2, Last = 0; for (; ; cnt ++) { Maketag(t); lst[t] = p; Last = max(Last, t); if (cnt == len) break; int tmp = Next(t); if (!tmp) break; t = tmp; if (cnt == (len + (l[i] & 1)) / 2) p = (l[i] & 1) ? Next(s1) : s1; else p = Next(Next(p)); } int del = 0; for (int i = 1; i <= top; i ++) { p = i - del; D[p] = D[i]; if (Find(D[p].r) == Find(s2)) del ++; } top -= del; D[++ top] = Coor[Find(s2)], p = top; for (; p > 1 && D[p].l < D[p - 1].l; p --) swap(D[p], D[p - 1]); } int get = 0; for (int i = 1; i <= k; i ++) { if (lst[i] == i) { get ++; T[i] = S[get]; } else T[i] = T[lst[i]]; printf("%c", T[i]); } printf("\n"); }
相关文章推荐
- JZOJ4744. 【NOIP2016提高A组模拟9.2】同余 一类比较好的分类思想+根号算法
- JZOJ.4694【NOIP2016提高A组模拟8.14】火神的鱼
- JZOJ 4699 Password【NOIP2016提高A组模拟8.15】
- JZOJ 4710 Value【NOIP2016提高A组模拟8.17】
- [jzoj4711]【NOIP2016提高A组模拟8.17】Binary
- JZOJ4735【NOIP2016提高A组模拟8.24】最小圈 Spfa深搜判负环
- 【JZOJ4743】【NOIP2016提高A组模拟9.2】积木
- JZOJ 4715 【NOIP2016提高A组模拟8.19】树上路径
- JZOJ4675. 【NOIP2016提高A组模拟7.21】Double-row
- 【JZOJ4763】【NOIP2016提高A组模拟9.7】旷野大计算
- JZOJ 4786. 【NOIP2016提高A组模拟9.17】小a的强迫症
- {题解}[jzoj4798] 【NOIP2016提高A组模拟9.24】天使的分裂
- JZOJ 4628 立方体【NOIP2016提高A组模拟7.15】
- JZOJ 4629 修路【NOIP2016提高A组模拟7.15】
- JZOJ 4819 【NOIP2016提高A组模拟10.15】算循环
- JZOJ [4820]. 【NOIP2016提高A组模拟10.15】最大化
- 【JZOJ4744】【NOIP2016提高A组模拟9.2】同余
- 【JZOJ4783】【NOIP2016提高A组模拟9.15】Osu
- 【JZOJ4799】【NOIP2016提高A组模拟9.24】我的快乐时代