CF590E Birthday

https://www.luogu.com.cn/problem/CF590E

本质上就是要求最长反链

首先可以用AC自动机建出一个DAG(可以用路径压缩实现)

然后对这个DAG求最长反链=最小可重复路径点覆盖=n-拆点二分图最大匹配

构造方案的话如果一个点不是匹配点,那么就是最长反链上的点
具体证明不太会

code:

#include<bits/stdc++.h>
#define N 10000005
#define INF 2147483646 / 2
using namespace std;
struct edge {
	int v, nxt, c;
} e[ 1505 * 1505 * 2];
int p[1505], eid;
void init() {
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v, int c) {
	e[eid].v = v;
	e[eid].c = c;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
void add(int u, int v, int c) {
	insert(u, v, c);
	insert(v, u, 0);
}
int ch[N][2], cnt[N], tot, ys[1505], nxt[N], g[755][755], L[1505], R[1505], ha[N];
string st;
queue<int> q;
void insert(int id) {
	int p = 0, len = st.length();
	L[id] = ha[0] + 1;
	for(int i = 0; i < len; i ++) { 
		ha[++ ha[0]] = st[i] - 'a';
		if(!ch[p][st[i] - 'a']) ch[p][st[i] - 'a'] = ++ tot;
		p = ch[p][st[i] - 'a'];
	}
//	printf("%d ", p);
	cnt[p] = id;
	R[id] = ha[0];
}
void build() {
	for(int i = 0; i < 2; i ++)
		if(ch[0][i]) q.push(ch[0][i]);
	while(q.size()) {
		int u = q.front(); q.pop();
		for(int i = 0; i < 2; i ++) {
			if(!ch[u][i]) ch[u][i] = ch[nxt[u]][i];
			else nxt[ch[u][i]] = ch[nxt[u]][i], q.push(ch[u][i]);
		}
	}
}
int vis[N];
void run(int id) { //printf("%d %d\n", L[id], R[id]);
	int p = 0;
	vis[0] = 1;
	for(int i = L[id]; i <= R[id]; i ++) {
		p = ch[p][ha[i]];//printf("*%d*", p);
		int pp = p, tt;
		while(pp) { //printf("%d ", pp);
			if(cnt[pp] && cnt[pp] != id) {
				g[id][cnt[pp]] = 1;// printf("%d --> %d\n", id, cnt[pp]);
				break;
			}
			pp = nxt[pp];
		}
		int haha = pp;
		pp = p;
		while(pp) { //printf("%d ", pp);
			if(cnt[pp] && cnt[pp] != id) {
				break;
			}	
			tt = pp;
			pp = nxt[pp];
			nxt[tt] = haha;
		
		}
	}
}


int dis[1505], S, T, n;
int bfs() {
	memset(dis, -1, sizeof dis);
	dis[S] = 1;
	q.push(S);
	while(q.size()) {
		int u = q.front(); q.pop();
		for(int i = p[u]; i + 1; i = e[i].nxt) {
			int v = e[i].v;
			if(dis[v] == -1 && e[i].c) {
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
	}
	return dis[T] != -1;
}
int dfs(int u, int flow) { //printf("%d %d\n", u, flow);
	if(u == T) return flow;
	int ret = 0;
	for(int i = p[u]; i + 1; i = e[i].nxt) {
		int c = e[i].c, v = e[i].v;
		if(c && dis[v] == dis[u] + 1) {
			int tmp = dfs(v, min(flow, c));
			ret += tmp, flow -= tmp;
			e[i].c -= tmp, e[i^1].c += tmp;
			if(!flow) break;
		}
	}
	if(!ret) dis[u] = -1;
	return ret;
}
int Dinic() {
	int ret = 0;
	for(;bfs();) ret += dfs(S, INF);
	return ret;
}
int main() {
	ios::sync_with_stdio(false);
	init();
	cin >> n; T = 2 * n + 1;
	for(int i = 1; i <= n; i ++) cin >> st, insert(i);
	build();
	for(int i = 1; i <= n; i ++) run(i);
	for(int k = 1; k <= n; k ++) 
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= n; j ++)
				g[i][j] |= (g[i][k] & g[k][j]);
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			if(g[i][j] && i != j) add(i, j + n, 1);
	for(int i = 1; i <= n; i ++) add(S, i, 1), add(i + n, T, 1), vis[i + n] = 0;
	
    // for(int i = 1; i <= n; i ++) {
    //     for(int j = 1; j <= n; j ++) printf("%d ", g[i][j]); printf("\n");
    // }
    cout << n - Dinic() << endl;
	for(int i = 1; i <= n; i ++) if(dis[i] != -1 && dis[i + n] == -1) cout << i << ' ';
	return 0;
}
posted @ 2021-12-10 11:13  lahlah  阅读(85)  评论(0编辑  收藏  举报