题解:CF590E Birthday

Tag:二分图匹配,AC 自动机,Dilworth,Konig。

题意:给定 nn 个互不相同字符串 sis_i。求最多能保留多少个字符串,使得两两之间互不包含。

范围:n750n \leq 750si107\sum \left| s_i \right| \leq 10^7,字符串只包含 ab\texttt{ab}

解法:先考虑求出任意两个字符串之间的包含关系。这一部分可以通过建立 AC 自动机求出。

具体来说,建立 AC 自动机,其上的字符串结尾位置只有 nn 个。我们称这些点为关键点。考虑对于 Fail 树上每个点,求出第一个非自身的祖先关键点,然后就可以将树简化,从而使 Fail 树变成只有 O(n)O(n) 个节点,然后就可以用一般的暴力跳 Fail 维护即可。

事实上容易发现这个包含关系是偏序关系,我们要求最长反链,根据 Dilworth 定理变成最小路径覆盖,然后变成了 DAG 上的最小路径覆盖,直接拆点变成二分图匹配问题。答案为 nn 减最大匹配。

至于构造答案,根据 Konig 定理可以将最大匹配变成最小点覆盖,然后参考 Dilworth 的证明部分即知,对于最长反链 AAiA    uiVCviVCi \in A \iff u_i \notin VC \land v_i \notin VCuuvv 分别是拆点出来的两部分,VCVC 是最小点覆盖集。

复杂度 O(n3+si)O(n^3+\sum \left| s_i \right|)

代码:

#pragma GCC optimize("-Ofast,fast-math,-inline")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <bitset>
#include <string>
#include <queue>
#include <vector>
using namespace std;

const int T = 755, N = 1505, M = 1e7 + 5;

int n;
string s[N];
bool g[T][T];

class ACAM
{
public:
	int son[M][2];
	// fa->fail
	int pos[M]; // 每个点往上第一个标记点祖先
	int fail[M], idx;
	int flag[M];
	bitset<M> rt;
	vector<int> G[N];
	void ins(string& s, int j)
	{
		int u = 0;
		for (auto& i : s)
		{
			int j = i - 'a';
			if (!son[u][j]) son[u][j] = ++idx;
			u = son[u][j];
		}
		flag[u] = j;
	}
	void build()
	{
		queue<int> q;
		if (son[0][0]) q.push(son[0][0]);
		if (son[0][1]) q.push(son[0][1]);
		while (q.size())
		{
			int u = q.front();
			q.pop();
			for (auto& i : { 0, 1 })
			{
				if (son[u][i])
				{
					fail[son[u][i]] = son[fail[u]][i];
					q.push(son[u][i]);
				}
				else son[u][i] = son[fail[u]][i];
			}
		}
	}
	void solve()
	{
		memset(pos, -1, sizeof pos);
		for (int i = 1; i <= idx; i++)
		{
			int fa = fail[i];
			if (!fa) pos[i] = 0;
			else
			{
				if (flag[fa])
				{
					pos[i] = fa;
					continue;
				}
				int j = fa;
				vector<int> vec;
				while (!flag[j] && j)
				{
					vec.emplace_back(j);
					if (~pos[j]) j = pos[j];
					else j = fail[j];
				}
				pos[i] = j;
				for (auto& k : vec) pos[k] = j;
			}
		}
		for (int i = 1; i <= idx; i++)
		{
			if (flag[i])
			{
				// fa=pos[i]
				int fa = pos[i];
				if (fa)
				{
					G[flag[fa]].emplace_back(flag[i]);
					rt[flag[i]] = 1;
				}
			}
		}
	}
	void match(string& s, int id)
	{
		int u = 0;
		for (auto& c : s)
		{
			u = son[u][c - 'a'];
			if (flag[u]) g[flag[u]][id] = 1;
			int f = pos[u];
			if (f)
			{
				g[flag[f]][id] = 1;
			}
		}
	}
	void dfs(int u)
	{
		for (auto& j : G[u])
		{
			dfs(j);
			for (int k = 1; k <= n; k++) if (g[j][k]) g[u][k] = 1;
		}
	}
	void build_graph()
	{
		for (int i = 1; i <= n; i++)
		{
			if (!rt[i])
			{
				dfs(i);
			}
		}
	}
}acam;

int match[N], pre[N];
bitset<N> vis;
bitset<N> vertexcover;

inline bool find_path()
{
	for (int i = 1; i <= 2 * n; i++) pre[i] = vis[i] = 0;
	queue<int> q;
	for (int i = 1; i <= n; i++) if (!match[i]) q.push(i);
	while (q.size())
	{
		int u = q.front();
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int j = n + 1; j <= 2 * n; j++)
		{
			if (j == match[u] || pre[j] || !g[u][j - n]) continue;
			pre[j] = u;
			int k = match[j];
			vis[j] = 1;
			if (!k) // 增广路
			{
				int x = j;
				while (true)
				{
					int y = pre[x], z = match[y];
					match[y] = x, match[x] = y;
					if (!z) return 1;
					x = z;
				}
			}
			else
			{
				if (!vis[k]) q.push(k);
			}
		}
	}
	return 0;
}

inline void solve()
{
	find_path();
	for (int i = n + 1; i <= 2 * n; i++) if (vis[i]) vertexcover[i] = 1;
	for (int i = 1; i <= n; i++) if (!vis[i]) vertexcover[i] = 1;
}

int main() // 1
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> s[i];
	for (int i = 1; i <= n; i++) acam.ins(s[i], i);
	acam.build();
	acam.solve();
	for (int i = 1; i <= n; i++)
	{
		acam.match(s[i], i);
	}
	acam.build_graph();
	for (int i = 1; i <= n; i++)
	{
		g[i][i] = 0;
	}
	int ans = 0;
	while (find_path()) ans++;
	cout << n - ans << "\n";
	solve();
	for (int i = 1; i <= n; i++)
	{
		if (!vertexcover[i] && !vertexcover[i + n]) cout << i << " ";
	}
	cout << "\n";
	return 0;
}
posted @ 2024-03-29 22:03  HappyBobb  阅读(4)  评论(0编辑  收藏  举报  来源