【题解】JZOJ 7867 字符串

题意

给定串 s s s n n n 次操作,每次依次写下 [ l , r ] [l,r] [l,r] 的奇数下标字符,再写 [ l , r ] [l,r] [l,r] 的偶数下标字符,最后将写下的串插入到 r r r 位字符后。问最后串的前 k k k 位。

正解

考虑倒着做,操作离线。发现对于一次操作就是删除一个对应的串。考虑删除后的串的字符在原串中的位置。直接拿一个线段树维护,初始全是 1 1 1,删除段区间赋值为 0 0 0,这样就可以查找第 k k k 大,删除后的第 k k k 个字符就是前缀和第 k k k 大。

将最终相同的位置用并查集串起来。

最后找到 s s s 的每个字符在最终的串中的位置。然后并查集查找会连到这些点上。

最终每个点都能找到对应的字符,因为并查集合并是由删掉的连向没删的,所以最后的根一定是没有被删掉的,即原串。

时间复杂度 O ( n   α ( k ) log ⁡ k ) O(n\space\alpha(k)\log k) O(n α(k)logk)

实现

#include <bits/stdc++.h>
using namespace std;
const int N = 3000005;
int n, K, l[5005], r[5005], tr[N << 3], lz[N << 3], fa[N];
char s[N];
int col[N];
#define ls rt << 1
#define rs rt << 1 | 1
void pushdown(int rt) { if (lz[rt]) tr[ls] = tr[rs] = 0, lz[ls] = lz[rs] = 1, lz[rt] = 0; }
void bdtr(int rt, int x, int y) {
	if (x == y) { tr[rt] = 1; return; }
	int mid = x + y >> 1;
	bdtr(ls, x, mid), bdtr(rs, mid + 1, y);
	tr[rt] = tr[ls] + tr[rs];
}
void update(int rt, int x, int y, int l, int r) {
	if (x > r || y < l) return;
	pushdown(rt);
	if (l <= x && y <= r) { tr[rt] = 0, lz[rt] = 1; return; }
	int mid = x + y >> 1;
	update(ls, x, mid, l, r), update(rs, mid + 1, y, l, r);
	tr[rt] = tr[ls] + tr[rs];
}
int query(int rt, int x, int y, int k) {
	if (x == y) return x;
	int mid = x + y >> 1;
	if (k <= tr[ls]) return query(ls, x, mid, k);
	else return query(rs, mid + 1, y, k - tr[ls]);
}
#undef ls
#undef rs
int findfa(int x) { return !fa[x] ? x : fa[x] = findfa(fa[x]); }
int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	scanf("%s%d%d", s + 1, &K, &n), bdtr(1, 1, K);
	for (int i = 1; i <= n; i++) scanf("%d%d", &l[i], &r[i]);
	int tk = K;
	for (int i = n; i >= 1; i--) {
		if (r[i] >= tk) continue;
		int x = r[i] + 1, y = min(tk, r[i] + r[i] - l[i] + 1);
		int j = x;
		for (int p = l[i] + 1 - (l[i] & 1); j <= y && p <= r[i]; j++, p += 2) {
			int f1 = findfa(query(1, 1, K, j)), f2 = findfa(query(1, 1, K, p));
			if (f1 != f2) fa[f1] = f2;
		}
		for (int p = l[i] + (l[i] & 1); j <= y && p <= r[i]; j++, p += 2) {
			int f1 = findfa(query(1, 1, K, j)), f2 = findfa(query(1, 1, K, p));
			if (f1 != f2) fa[f1] = f2;
		}
		update(1, 1, K, query(1, 1, K, x), query(1, 1, K, y)), tk -= y - x + 1;
	}
	int M = strlen(s + 1);
	for (int i = 1; i <= M; i++) col[query(1, 1, K, i)] = i;
	for (int i = 1; i <= K; i++) printf("%c", s[col[findfa(i)]]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值