P5284 [十二省联考2019]字符串问题

题目链接

SA字典序排序二分 + 线段树优化建图 + 判环 + 拓扑DP最长路

省选前一天做十二省联考两天T2,当模拟省选,结果写了俩十分部分分还都爆零了(因为一个数组少开了个零,一个似乎特判那里有问题),省选爆炸也可能和这个有关系,今天补上。

首先转化成最长路模型还是比较容易的,至少省选前我就能想到。A -(支配)- > B, B -(前缀)- > A,然后 S -> A, A -> T,跑一个从 \(S\)\(T\) 的最长路即可。如果有环就是-1(因为边权均非负且无零环)

其中第1,3,4种边比较好搞,点数 \(O(n)\),边数 \(O(m)\)。但是第 2 种边很麻烦,需要一个B点向所有包含B前缀的A点连边,最大边数能达到 \(O(n^2)\) 的级别。那么我们需要考虑优化建图。

子串问题想后缀系列。看起来 SAM 很不可做的样子,我们考虑 SA。如果一些 \(A\) 点包含 \(B\) 前缀,那么这些 \(A\) 点所在的原串后缀一定是一段连续的区间(这个可能有点tbl),但是存在 \(A\)\(B\) 的子串的情况的话将会有一些“缺口”。不过我们发现如果那样的话 \(A\)字典序(不是原串后缀字典序)一定比 \(B\) 的小,那么我们干脆直接按子串的字典序\(A\) 排序,查询可以直接二分(lower_bound + half_find)。再加上线段树优化建图,我们就做完了。

注意一下,这里最长路直接拓扑DP即可,不用SPFA,因为环可以判掉,就直接成一DAG了。

思维难度省选-,代码难度NOI

上6.5K代码的部分代码:

inline void Build()
inline int get_lcp(int l, int r) //rank l ~ rank r
int na, nb;
struct Substring {
	int l, r, id;
	inline void Read(int idd) {
		read(l), read(r); id = idd;
	}
	bool operator <(const Substring a) const {
		int lcp = get_lcp(rk[l], rk[a.l]);
		int len1 = r - l + 1, len2 = a.r - a.l + 1;
		if (lcp < len1 && lcp < len2)	return s[l + lcp] < s[a.l + lcp];//Attention!!! : <
//		if (lcp >= len1 && lcp >= len2)	return ;//Attention!!! : > //Attention!!!!
		return len1 < len2;
	}
}A[N], B[N];
int mp[N], pos[N];
int ls[NN], rs[NN], root, ttot;
void build(int L, int R, int &cur)
void AddEdge(int L, int R, int l, int r, int from, int cur)
int dfn[NN + N], low[NN + N], dcnt;
int stk[NN + N], stop, col[NN + N], ctot;
bool instk[NN + N];
void tarjan(int cur)

int S, t;
int que[NN + N], front, rear;
ll dis[NN + N];
inline void get_ans() {
	memset(dis, 0xc0, sizeof(dis));
	dis[S] = 0;
	for (register int i = 1; i <= t; ++i)	if (!d[i])	que[++rear] = i;//Attention!!!!
	while (front < rear) {
		int cur = que[++front];
		for (register int i = head[cur]; i; i = e[i].nxt) {
			int to = e[i].to;
			MAX(dis[to], dis[cur] + e[i].val);
			--d[to];
			if (!d[to])	que[++rear] = to;
		}
	}
	printf("%lld\n", dis[t]);
}
inline int Find(int st, int l, int len) {//Attention!!!!!
	int r = na, res = l - 1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		int lcp = get_lcp(rk[A[mid].l], rk[B[st].l]);//Attention!!!!!!
		if (lcp >= len)	res = mid, l = mid + 1;
		else	r = mid - 1;
	}
	return res;
}
inline void work() {
	scanf("%s", s + 1); slen = strlen(s + 1);
	Build();
	read(na);
	for (register int i = 1; i <= na; ++i) A[i].Read(i);
	read(nb);
	for (register int i = 1; i <= nb; ++i)	B[i].Read(i);
	sort(A + 1, A + 1 + na);
	for (register int i = 1; i <= na; ++i)	mp[A[i].id] = i;
	build(1, na, root);
	int m; read(m);
	for (register int i = 1; i <= m; ++i) {
		int a, b; read(a), read(b); a = mp[a];
		addedge(pos[a], ttot + b, A[a].r - A[a].l + 1);
	}
	for (register int i = 1; i <= nb; ++i) {
		int l = lower_bound(A + 1, A + 1 + na, B[i]) - A;
		int r = Find(i, l, B[i].r - B[i].l + 1);//Attention!!!!!!
		if (l > r)	continue;
		AddEdge(1, na, l, r, i + ttot, root);
	}
	S = ttot + nb + 1, t = S + 1;
	for (register int i = 1; i <= na; ++i) {
		addedge(S, pos[i], 0);
		addedge(pos[i], t, A[i].r - A[i].l + 1);
	}
	for (register int i = 1; i <= t; ++i) {
		if (!dfn[i])	tarjan(i);
	}
	if (ctot != t) { puts("-1"); return ; }
	get_ans();
}
posted @ 2020-08-17 19:56  JiaZP  阅读(148)  评论(2编辑  收藏  举报