• 【洛谷5284】[十二省联考2019] 字符串问题(后缀树优化建边)


    题目:

    洛谷 5284

    分析:

    首先不要问我标题里的「后缀树」是什么,我也不会,瞎写的 …… (传说就是反串后缀自动机的 fa 树?)

    前置技能:【知识总结】后缀自动机的构建

    首先有一个很 naive 的想法:要求的 (T) 是由若干个 (A_i) 串拼接而成的,所以可以处理出对于每个 (A_i) ,后面接哪些 (A_j) 是合法的。将每个 (A) 串看成一个权值为串长的点, (A_i) 后面可以接 (A_j) 看成一条边 ((i,j)) ,这样能建出一个 DAG ,DP 出最长路就是答案。一个小优化是把每个 (B) 串也看作一个点(权值为 (0) ),用后缀自动机之类的东西暴力处理出每个 (B_i) 是哪些 (A_j) 的前缀并连边,同时每个 (A) 向其支配的 (B) 连边。但这样边数最坏仍然是 (O(n^2)) 的。

    等等,刚才好像写了个「后缀自动机」?首先,可以通过树上倍增求出每个 (A)(B) 串在后缀自动机上的点。「 (B_i)(A_j) 的前缀」在「后缀」自动机上不太好搞,不如用 (S) 的反串来建 SAM ,这个条件就变成了 (B_i)(A_j) 的后缀。那么,如果一个点 (A_j)(B_i) 所在结点的子树( fa 树)内,说明 (B_i)(A_j) 的后缀,需要连边。由此可知,我们需要从上至下连出 fa 树(即每个结点的 fa 向这个结点连边),然后每个 (B) 串向对应结点连边,每个结点向这个结点上的 (A) 串连边。

    但是这样做有一个小瑕疵:如果 (A_i)(B_j) 在同一个结点上,那么它们之间是否连边取决于它们的长短关系,即这些串中短的一定是长的的后缀。因此,较长的 (B_j) 能连到的 (A_i) ,较短的 (B_k) 也一定能连到。此时的做法是把 (B) 按照从短到长连成一条链,然后对于 (A_i) ,找出最长的长度不超过它的 (B_j) ,并由 (B_j)(A_i) 连边。注意,既然如此,上文中说的「所在结点的子树」就不包含这个点本身(因为这个点上的 (A) 不一定全部满足),所以需要把树上的每个结点拆成出点和入点,入点向出点连,父亲的出点向儿子的入点连,入点向 (A) 连,(B) 向出点连。

    代码:

    恐龙给说的条件编译真好用

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cctype>
    #include <vector>
    #include <queue>
    using namespace std;
    
    namespace zyt
    {
    	template<typename T>
    	inline bool read(T &x)
    	{
    		char c;
    		bool f = false;
    		x = 0;
    		do
    			c = getchar();
    		while (c != EOF && c != '-' && !isdigit(c));
    		if (c == EOF)
    			return false;
    		if (c == '-')
    			f = true, c = getchar();
    		do
    			x = x * 10 + c - '0', c = getchar();
    		while (isdigit(c));
    		if (f)
    			x = -x;
    		return true;
    	}
    	inline bool read(char *const s)
    	{
    		return ~scanf("%s", s);
    	}
    	template<typename T>
    	inline void write(T x)
    	{
    		static char buf[20];
    		char *pos = buf;
    		if (x < 0)
    			putchar('-'), x = -x;
    		do
    			*pos++ = x % 10 + '0';
    		while (x /= 10);
    		while (pos > buf)
    			putchar(*--pos);
    	}
    	typedef long long ll;
    	const int N = 2e5 + 10, P = N * 6, M = N * 8, LOG = 20, CH = 26;
    	const bool A = true, B = false;// B should be front of A
    	int head[P], ecnt, n, m, na, nb, w[P];
    	struct edge
    	{
    		int to, next;
    	}e[M];
    	void add(const int a, const int b)
    	{
    		e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
    	}
    	typedef pair<int, bool> pib;
    	typedef pair<pib, int> ppi;
    	vector<ppi> v[N << 1];
    	namespace Suffix_Auto_Chicken
    	{
    		struct node
    		{
    			int fa, max, s[CH];
    		}tree[N << 1];
    		int cnt, last, pos[N];
    		void init()
    		{
    			memset(tree, 0, sizeof(node[cnt + 1]));
    			for (int i = 0; i <= cnt; i++)
    				v[i].clear();
    			cnt = last = 1;
    		}
    		inline int ctoi(const char c)
    		{
    			return c - 'a';
    		}
    		void insert(const char c, const int id)
    		{
    			int x = ctoi(c);
    			int np = ++cnt, p = last;
    			tree[np].max = tree[p].max + 1;
    			while (p && !tree[p].s[x])
    				tree[p].s[x] = np, p = tree[p].fa;
    			if (!p)
    				tree[np].fa = 1;
    			else
    			{
    				int q = tree[p].s[x];
    				if (tree[p].max + 1 == tree[q].max)
    					tree[np].fa = q;
    				else
    				{
    					int nq = ++cnt;
    					memcpy(tree[nq].s, tree[q].s, sizeof(int[CH]));
    					tree[nq].fa = tree[q].fa;
    					tree[np].fa = tree[q].fa = nq;
    					tree[nq].max = tree[p].max + 1;
    					while (p && tree[p].s[x] == q)
    						tree[p].s[x] = nq, p = tree[p].fa;
    				}
    			}
    			last = np;
    			pos[id] = np;
    		}
    		int buf[N << 1], fa[N << 1][LOG];
    		void topo()
    		{
    			static int count[N];
    			int mx = 0;
    			memset(count, 0, sizeof(count));
    			for (int i = 1; i <= cnt; i++)
    				++count[tree[i].max], mx = max(mx, tree[i].max);
    			for (int i = 1; i <= mx; i++)
    				count[i] += count[i - 1];
    			for (int i = 1; i <= cnt; i++)
    				buf[count[tree[i].max]--] = i;
    		}
    		void build(const char *const s)
    		{
    			init();
    			for (int i = 0; i < n; i++)
    				insert(s[i], i);
    			topo();
    			for (int i = 1; i <= cnt; i++)
    			{
    				fa[buf[i]][0] = tree[buf[i]].fa;
    				for (int j = 1; j < LOG; j++)
    					fa[buf[i]][j] = fa[fa[buf[i]][j - 1]][j - 1];
    			}
    		}
    		int get(const int l, const int r)
    		{
    			int u = pos[r];
    			for (int i = LOG - 1; i >= 0; i--)
    				if (fa[u][i] && tree[fa[u][i]].max >= r - l + 1)
    					u = fa[u][i];
    			return u;
    		}
    	}
    	char s[N];
    	using namespace Suffix_Auto_Chicken;
    	ll solve(const int n)
    	{
    		static queue<int> q;
    		static int in[P];
    		static ll f[P];
    		static bool vis[P];
    		while (!q.empty())
    			q.pop();
    		memset(in, 0, sizeof(int[n + 1]));
    		memset(vis, 0, sizeof(bool[n + 1]));
    		memset(f, 0, sizeof(ll[n + 1]));
    		for (int i = 0; i < ecnt; i++)
    			++in[e[i].to];
    		for (int i = 1; i <= n; i++)
    			if (!in[i])
    				q.push(i), f[i] = w[i];
    		ll ans = 0;
    		while (!q.empty())
    		{
    			int u = q.front();
    			q.pop();
    			ans = max(ans, f[u]);
    			for (int i = head[u]; ~i; i = e[i].next)
    			{
    				int v = e[i].to;
    				if (vis[v])
    					return -1;
    				f[v] = max(f[v], f[u] + w[v]);
    				if (!(--in[v]))
    					q.push(v), vis[v] = true;
    			}
    		}
    		for (int i = 1; i <= n; i++)
    			if (in[i])
    				return -1;
    		return ans;
    	}
    	int work()
    	{
    		int T;
    		read(T);
    		while (T--)
    		{
    			read(s), n = strlen(s);
    			reverse(s, s + n);
    			build(s);
    			for (int i = 1; i <= cnt; i++)
    				w[i] = w[i + cnt] = 0;
    			read(na);
    			for (int i = 1; i <= na; i++)
    			{
    				int l, r;
    				read(l), read(r);
    				l = n - l, r = n - r, swap(l, r);
    				v[get(l, r)].push_back(ppi(pib(r - l + 1, A), i));
    				w[i + cnt * 2] = r - l + 1;
    			}
    			read(nb);
    			for (int i = 1; i <= nb; i++)
    			{
    				int l, r;
    				read(l), read(r);
    				l = n - l, r = n - r, swap(l, r);
    				v[get(l, r)].push_back(ppi(pib(r - l + 1, B), i));
    				w[i + cnt * 2 + na] = 0;
    			}
    			memset(head, -1, sizeof(int[na + nb + cnt * 2 + 1]));
    			ecnt = 0;
    			for (int i = 1; i <= cnt; i++)
    			{
    				sort(v[i].begin(), v[i].end());
    				add(i + cnt, i);
    				if (tree[i].fa)
    					add(tree[i].fa, i + cnt);
    				int last = -1;
    				for (vector<ppi>::iterator it = v[i].begin(); it != v[i].end(); it++)
    				{
    					if (it->first.second == A)
    					{
    						add(i + cnt, it->second + cnt * 2);
    						if (~last)
    							add(last + cnt * 2 + na, it->second + cnt * 2);
    					}
    					else
    					{
    						add(it->second + cnt * 2 + na, i);
    						if (~last)
    							add(last + cnt * 2 + na, it->second + cnt * 2 + na);
    						last = it->second;
    					}
    				}
    			}
    			read(m);
    			while (m--)
    			{
    				int a, b;
    				read(a), read(b);
    				add(a + cnt * 2, b + cnt * 2 + na);
    			}
    			write(solve(cnt * 2 + na + nb)), putchar('
    ');
    		}
    		return 0;
    	}
    }
    int main()
    {
    #ifdef BlueSpirit
    	freopen("5284.in", "r", stdin);
    #endif
    	return zyt::work();
    }
    
  • 相关阅读:
    补发《超级迷宫》站立会议九
    补发《超级迷宫》站立会议八
    补发《超级迷宫》站立会议七
    补发《超级迷宫》站立会议六
    一周开发项目
    所学的内容
    开发项目和所用时间 感想
    自我介绍
    大容量数据转移操作命令——BULK INSERT(类似于BCP)
    字符编码与文件处理
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/10912559.html
Copyright © 2020-2023  润新知