zoukankan      html  css  js  c++  java
  • day16T3改错记

    题目描述

    (n)个串,你要把每个串用它的一个子序列(可以不连续)替换,使得最终不存在两个串相同,求替换后最长串的最小长度,以及替换方案

    输入的串可能有相同的

    (n le 300),每个原串长度不超过(300)

    解析

    “最长的长度最小”套路二分

    问题变成判断是否存在一种合法方案使得替换后所有串长度不超过(mid)

    因为是用子序列替换,先建出子序列自动机

    然后因为一个新串只能替换一个原串,所以考虑二分图

    对每个原串,找出它的长度不超过的(mid)的子序列,并将原串同这个子序列代表的点连边

    注意到如果长度满足条件的子序列不少于(n)个,那么这个原串一定可以匹配上一个新串,所以每个原串最多只需要找(n)个合法子序列就可以了

    找出的所有新串构成一棵(trie)(trie)每个节点都对应了新串,所以点数不超过(n^2),可以用(trie)的点编号来表示新串

    然后匈牙利或者(dinic)判断是否存在完备匹配

    复杂度(O(n^3log maxlen))

    代码

    警告:代码极度丑陋,我自己都看不下去了,有时间再重构吧

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #define MAXN 305
    
    typedef long long LL;
    struct Trie {
    	int fa[MAXN * MAXN], trans[MAXN * MAXN][MAXN], ch[MAXN * MAXN], idx;
    	int newnode(int = 0, int = 0);
    	void init();
    	int insert(char *);
    	void print(int);
    };
    struct Graph {
    	struct Edge {
    		int v, next;
    		Edge(int _v = 0, int _n = 0):v(_v), next(_n) {}
    	} edge[MAXN * MAXN];
    	int head[MAXN], cnt, pre[MAXN], link[MAXN * MAXN];
    	bool vis[MAXN * MAXN];
    	void init() { memset(head, -1, sizeof head); memset(link, -1, sizeof link); cnt = 0; }
    	void add_edge(int u, int v) { edge[cnt] = Edge(v, head[u]); head[u] = cnt++; }
    	bool match(int);
    };
    
    void prework();
    bool check(int);
    void dfs(int, int, int &, int, int, int);
    
    int N, next[MAXN][MAXN][26], len[MAXN], maxlen, endp[MAXN * MAXN], tot, top;
    char name[MAXN][MAXN], str[MAXN];
    Trie trie;
    Graph G;
    
    int main() {
    	freopen("diff.in", "r", stdin);
    	freopen("diff.out", "w", stdout);
    
    	scanf("%d", &N);
    	for (int i = 0; i < N; ++i) {
    		scanf("%s", name[i] + 1);
    		len[i] = strlen(name[i] + 1) + 1;
    		maxlen = std::max(maxlen, len[i]);
    	}
    	prework();
    	int l = 1, r = maxlen;
    	while (l < r) {
    		int mid = (l + r) >> 1;
    		if (check(mid)) r = mid;
    		else l = mid + 1;
    	}
    	if (check(l)) {
    		printf("%d
    ", l);
    		for (int i = 0; i < N; ++i) trie.print(G.pre[i]), putchar('
    ');
    	} else puts("-1");
    
    	return 0;
    }
    void Trie::init() { idx = 0; newnode(); }
    int Trie::newnode(int f, int c) {
    	++idx;
    	for (int i = 0; i < 26; ++i) trans[idx][i] = 0;
    	fa[idx] = f, ch[idx] = c;
    	return idx;
    }
    void Trie::print(int x) {
    	if (fa[x] ^ 1) print(fa[x]);
    	putchar('a' + ch[x]);
    }
    void prework() {
    	for (int i = 0; i < N; ++i) for (int j = len[i] - 1; j >= 0; --j) for (int k = 0; k < 26; ++k)
    		next[i][j][k] = (name[i][j + 1] - 'a' == k ? j + 1 : next[i][j + 1][k]);
    }
    bool check(int m) {
    	G.init(); trie.init();
    	tot = N - 1;
    	for (int i = 0; i < N; ++i) {
    		int rest = N;
    		dfs(i, 0, rest, m, 0, 1);
    	}
    	for (int i = 0; i < N; ++i) {
    		memset(G.vis, 0, sizeof G.vis);
    		if (!G.match(i)) return 0;
    	}
    	return 1;
    }
    void dfs(int id, int p, int &rest, int m, int dep, int cur) {
    	if (!rest) return;
    	if (dep == m) return;
    	for (int i = 0; i < 26; ++i)
    		if (next[id][p][i]) {
    			if (!rest) break;
    			--rest;
    			if (!trie.trans[cur][i]) trie.trans[cur][i] = trie.newnode(cur, i);
    			G.add_edge(id, trie.trans[cur][i]);
    			dfs(id, next[id][p][i], rest, m, dep + 1, trie.trans[cur][i]);
    		}
    }
    bool Graph::match(int x) {
    	for (int i = head[x]; ~i; i = edge[i].next)
    		if (!vis[edge[i].v]) {
    			vis[edge[i].v] = 1;
    			if (!(~link[edge[i].v]) || match(link[edge[i].v])) {
    				link[edge[i].v] = x; pre[x] = edge[i].v;
    				return 1;
    			}
    		}
    	return 0;
    }
    //Rhein_E 100pts
    
  • 相关阅读:
    [牛客]十二桥问题 解题报告
    [NOIP2017 逛公园] 解题报告
    [JSOI2008]最小生成树计数 解题报告
    类欧几里得算法
    概率与期望题目列表
    [SCOI2008]配对 解题报告
    拦截导弹
    牛客网-约数的个数
    牛客网-成绩排名
    最大连续区间和的算法总结
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10561531.html
Copyright © 2011-2022 走看看