[SDOI2017]天才黑客(前后缀优化建图+Dijkstra)

题目:洛谷P3783LOJ#2270

题目描述:

给定\(n\)个点,\(m\)条边的有向图,还有一棵\(k\)个节点的树,保证有向图能从\(1\)号节点通过某些有向边到达任何图上的其他节点,通过图上的边\(i\)需要\(c_{i}\)时间,每条边有一个值\(d_{i}\),表示这条边上面的字符串\(S_{i}\)为从给定的\(k\)个节点的树的根节点一直到节点\(d_{i}\)的所有边上的字母连接起来构成的字符串,初始你的字符串为空,假设你现在的字符串为\(S\),经过一条边后,你需要消耗\(lcp(S, S_{d_{i}})\)时间使\(S=S_{d_{i}}\),求从\(1\)号节点所有其他节点的最少时间分别是多少

一共\(T\)组数据

\(T \leq 10\)\(n,m \leq 50000\)\(k \leq 20000\)

蒟蒻题解:

通过题意可得,两条边之间字符串的\(lcp\)相当于它们\(d_{i}\)\(lca\)的深度

容易想到化边为点,把每条边拆成入点和出点,但是这样边数最多会达到\(m^{2}\)级别的,会拿到\(0\)分的好成绩

有一个树上的性质,把树上的所有节点按\(dfs\)序排序,则\(lcp(S_{i},S_{j})=min(lcp(S_{i},S_{i+1}),lcp(S_{i},S_{i+1}),...,lcp(S_{j-1},S_{j}))\)

我看很多题解提到这个有点类似后缀数组,但是身为只打过后缀数组模板的蒟蒻,我是用\(RMQ\)\(lca\)的方法去理解的

取出一个图上的点\(u\)以及它周围的所有边,如下图

红点表示的是连向\(u\)的所有边的出点,蓝点表示从\(u\)连出去的所有边的入点

中间的点\(u\)没用,可以删去,就变成了\(m^{2}\)条边级别的暴力,如下图(太多边了,箭头就不打了,都是从上面指向下面)

根据我们已知的性质,我们可以把所有的这些点按\(dfs\)序排序,对于两个点之间的\(lcp(a_{i},a_{j})\)可以用\(min(lcp(a_{i},a_{i+1}),lcp(a_{i+1},a_{i+2}),...,lcp(a_{j-1},a_{j}))\)代替,对于\(lcp(a_{k},a_{k+1})\)的贡献只和\(i \leq k < j\)且满足一个为红点,一个为蓝点的\((i,j)\)有关,由于这题是要求最小路径,建完图后要跑最短路,所以如果\(lcp(a_{k},a_{k+1})\)是最小的,那么红点要经过这条边,所以可以让每个点都像与它同色的上一个点或下一个点连边,以使红点能到达连接权值为\(lcp(a_{k},a_{k+1})\)的红点,并通过这条边到这个权值连接的蓝点,再通过这个蓝点到达它要到达的目标蓝点,所以红点和红点间、蓝点和蓝点间要么都是向右,要么都是向左以满足一个红点和蓝点组成的点对满足\(i \leq k < j\),而在前缀优化建图中,\(lcp(a_{k},a_{k+1})\)权值所在边的位置就要满足上面的红点是小于等于\(a_{k}\)的最靠右的点,下面的蓝点是大于等于\(a_{k+1}\)的最靠做的点,后缀优化建图同样的道理,前缀优化建图是建满足\(i \leq k < j\),其中\(i\)为红点,\(j\)为蓝点的路径,而后缀优化建图是建满足\(i \leq k < j\),其中\(i\)为蓝点,\(j\)为红点的路径,前缀优化建图如下(注意前缀优化建图和后缀优化建图不能用相同的点,得新建节点):

前缀优化建图方式:对于\(lcp(a_{i},a{i+1})\),在红点找\(dfs\)序小于等于\(a_{i}\)的最后一个点,在蓝点找\(dfs\)序大于等于\(a_{i+1}\)的第一个点,然后这两个点连权值为\(lcp(a_{i},a_{i+1})\)的边

时间复杂度为\(\Theta((N+M)log\ M)\),这里的\(N\)\(n\)的很多倍(我的垃圾写法最多接近\(6\)倍),\(M\)\(m\)的很多倍(我的垃圾写法最多接近\(36\)倍),但是由于远远跑不满,再加上\(n、m\)又不是特别大,所以可以轻松地通过此题

参考程序:

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 20005;
const int M = 50005;
const int K = 300005;
const ll Inf = 1e10;
struct edge
{
	int x, y, t, w;
}eg[M];
struct inf
{
	int s;
	bool t;
}a[M];
struct info
{
	int id;
	ll dis;
	friend bool operator < (info x, info y)
	{
		return x.dis > y.dis;
	}
};
int T, n, m, k, num, cnt, cnt1, tim, siz, lg[M], hea1[N], nxt1[M], to1[M], fa[N], dfn[N], dep[N], g[M], f[M][19], pr[M][2], nt[M][2], id[K], hea[K], nxt[K * 6], to[K * 6], len[K * 6];
bool b[K];
ll dis[K], ans[M];
queue<int> q1[M], q2[M];
priority_queue<info> q;

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void write(ll x)
{
	int num = 0;
	char sc[25];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar('\n');
}

inline ll min(ll x, ll y)
{
	return x < y ? x : y;
}

inline void add(int x, int y, int z)
{
	nxt[++cnt] = hea[x], to[cnt] = y, len[cnt] = z, hea[x] = cnt;
}

inline void add1(int x, int y)
{
	nxt1[++cnt1] = hea1[x], to1[cnt1] = y, hea1[x] = cnt1;
}

inline void dfs(int x)
{
	dep[x] = dep[fa[x]] + 1, g[dfn[x] = ++tim] = x, f[tim][0] = dep[x];
	for (Re i = hea1[x]; i; i = nxt1[i])
	{
		int u = to1[i];
		if (u == fa[x]) continue;
		fa[u] = x;
		dfs(u);
		g[++tim] = x, f[tim][0] = dep[x];
	}
}

inline int lca(int x, int y)
{
	if (x == y) return dep[x];
	x = dfn[x], y = dfn[y];
	if (x > y) x ^= y ^= x ^= y;
	int z = lg[y - x + 1];
	return min(f[x][z], f[y - (1 << z) + 1][z]);
}

inline bool cmp(inf x, inf y)
{
	return dfn[eg[x.s].w] < dfn[eg[y.s].w];
}

inline void dij()
{
	for (Re i = 0; i <= num; ++i) dis[i] = Inf, b[i] = 0;
	q.push(info{0, dis[0] = 0});
	while (!q.empty())
	{
		int x = q.top().id;
		q.pop();
		if (b[x]) continue;
		b[x] = 1;
		for (Re i = hea[x]; i; i = nxt[i])
		{
			int u = to[i];
			if (dis[u] > dis[x] + len[i]) q.push(info{u, dis[u] = dis[x] + len[i]});
		}
	}
}

int main()
{
	T = read();
	for (Re i = 1; (1 << i) <= 4e4; ++i) lg[1 << i] = i;
	for (Re i = 3; i <= 4e4; ++i)
		if (!lg[i]) lg[i] = lg[i - 1];
	while (T--)
	{
		n = read(), m = read(), k = read(), num = cnt = cnt1 = tim = hea[0] = 0;
		for (Re i = 1; i <= n; ++i) ans[i] = Inf;
		for (Re i = 1; i <= k; ++i) hea1[i] = fa[i] = dfn[i] = 0;
		for (Re i = 1; i <= m; ++i)
		{
			int u = read(), v = read();
			eg[i].t = read(), eg[i].w = read();
			hea[num + 1] = hea[num + 2] = 0;
			add(eg[i].x = num + 1, eg[i].y = num + 2, eg[i].t), id[++num] = u, id[++num] = v;
			if (u == 1) add(0, eg[i].x, 0);
			q1[u].push(i), q2[v].push(i);
		}
		for (Re i = 1; i < k; ++i)
		{
			int u = read(), v = read();
			add1(u, v), add1(v, u);
			u = read();
		}
		dep[0] = -1;
		dfs(1);
		for (Re i = 1; i <= 17; ++i)
			for (Re j = 1; j <= tim - (1 << i) + 1; ++j)
				f[j][i] = min(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
		for (Re i = 1; i <= n; ++i)
		{
			siz = 0;
			while (!q1[i].empty()) a[++siz].s = q1[i].front(), a[siz].t = 1, q1[i].pop();
			while (!q2[i].empty()) a[++siz].s = q2[i].front(), a[siz].t = 0, q2[i].pop();
			sort(a + 1, a + siz + 1, cmp);
			pr[0][0] = pr[0][1] = nt[siz + 1][0] = nt[siz + 1][1] = 0;
			for (Re j = 1; j <= siz; ++j) pr[j][a[j].t] = j, pr[j][a[j].t ^ 1] = pr[j - 1][a[j].t ^ 1];
			for (Re j = siz; j; --j) nt[j][a[j].t] = j, nt[j][a[j].t ^ 1] = nt[j + 1][a[j].t ^ 1];
			for (Re j = num + 1; j <= num + (siz << 1); ++j) hea[j] = 0;
			for (Re j = 1; j <= siz; ++j)
				if (!a[j].t) id[num + j] = id[num + j + siz] = id[eg[a[j].s].y], add(eg[a[j].s].y, num + j, 0), add(eg[a[j].s].y, num + j + siz, 0);
				else id[num + j] = id[num + j + siz] = id[eg[a[j].s].x], add(num + j, eg[a[j].s].x, 0), add(num + j + siz, eg[a[j].s].x, 0);
			for (Re j = 1; j < siz; ++j)
			{
				if (pr[j][0] && pr[j][0] ^ pr[j + 1][0]) add(num + pr[j][0], num + pr[j + 1][0], 0);
				if (pr[j][1] && pr[j][1] ^ pr[j + 1][1]) add(num + pr[j][1], num + pr[j + 1][1], 0);
				if (pr[j][0] && nt[j + 1][1]) add(num + pr[j][0], num + nt[j + 1][1], lca(eg[a[j].s].w, eg[a[j + 1].s].w));
			}
			num += siz;
			for (Re j = 1; j < siz; ++j)
			{
				if (pr[j][0] && pr[j][0] ^ pr[j + 1][0]) add(num + pr[j + 1][0], num + pr[j][0], 0);
				if (pr[j][1] && pr[j][1] ^ pr[j + 1][1]) add(num + pr[j + 1][1], num + pr[j][1], 0);
				if (pr[j][1] && nt[j + 1][0]) add(num + nt[j + 1][0], num + pr[j][1], lca(eg[a[j].s].w, eg[a[j + 1].s].w));
			}
			num += siz;
		}
		dij();
		for (Re i = 1; i <= num; ++i) ans[id[i]] = min(ans[id[i]], dis[i]);
		for (Re i = 2; i <= n; ++i) write(ans[i]);
	}
	return 0;
}
posted @ 2021-03-12 21:02  clfzs  阅读(216)  评论(0)    收藏  举报