您的位置:首页 > 运维架构

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");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: