Loading

【题解】ABC237Ex - Hakata

我们将每个回文串作为一个节点,如果 \(T\)\(S\) 的子串,就连一条有向边 \(S\to T\),最后我们可以得到一个 DAG。

然后我们要选出最多的节点使得不存在一个节点能到达另一个节点。

首先我们直到节点数是 \(<2|S|\) 的,因为本质不同的回文串只会在 manacher 算法向又扩展时出现,而扩展最多 \(N\) 次。

其次考虑到 DAG 上并不方便 DP,考虑图论模型。我们直到有最大流最小割定理,这里求最大点集,可以联想到求 DAG 的最小链覆盖。根据 Dilworth 定理,最大反链元素个数等于最小链覆盖数。所以我们直接拆点二分图匹配即可。

#define N 205
#define M 405
int n, hs[N][N], v[N][N]; char a[N];
bool ck(int l,int r){
	int w = (r - l + 1) / 2;
	rp(i, w)if(a[l + i - 1] != a[r - i + 1])return false;
	return true;
}
map<int,int>c; map<Pr,int>o;
struct edge{int to, nxt, cap;}e[N * N];
int h[M], d[M], cur[M], tot = 1, s, t, idx;
queue<int>q;
void add(int x,int y,int z){
	e[++tot].nxt = h[x], h[x] = tot, e[tot].to = y, e[tot].cap = z;
	e[++tot].nxt = h[y], h[y] = tot, e[tot].to = x, e[tot].cap = 0;
}
bool bfs(){
	rp(i, t)cur[i] = h[i], d[i] = 0;
	while(!q.empty())q.pop();
	q.push(s), d[s] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		if(x == t)return true;
		for(int i = h[x]; i; i = e[i].nxt)if(e[i].cap && !d[e[i].to])
			d[e[i].to] = d[x] + 1, q.push(e[i].to);
	}
	return false;
}
int dfs(int x,int flow){
	if(x == t)return flow;
	int res = flow;
	for(int &i = cur[x]; i; i = e[i].nxt)if(e[i].cap && d[e[i].to] == d[x] + 1){
		int w = dfs(e[i].to, min(flow, e[i].cap));
		if(!w)d[e[i].to] = 0;
		else e[i].cap -= w, e[i ^ 1].cap += w, res -= w;
		if(!res)return flow;
	}
	return flow - res;
}
int main() {
	scanf("%s", a + 1);
	n = strlen(a + 1);
	rp(i, n)
		rep(j, i, n)v[i][j] = ck(i, j), hs[i][j] = (hs[i][j - 1] * 1LL * bas + a[j]) % P;
	rp(i, n)rep(j, i, n)if(v[i][j] && !c.count(hs[i][j]))c[hs[i][j]] = ++idx;
	rp(i, n)rep(j, i, n)rep(x, i, j)rep(y, x, j)if((i != x || y != j) && v[i][j] && v[x][y] && !o[mp(hs[i][j], hs[x][y])])
		add(c[hs[i][j]], idx + c[hs[x][y]], 1), o[mp(hs[i][j], hs[x][y])] = 1;
	s = idx + idx + 1, t = s + 1;
	rp(i, idx)add(s, i, 1), add(i + idx, t, 1);
	int ans = idx;
	while(bfs())ans -= dfs(s, idx);
	cout << ans << endl;
	return 0;
}
posted @ 2022-02-03 12:14  7KByte  阅读(155)  评论(0编辑  收藏  举报