zoukankan      html  css  js  c++  java
  • 「TJOI2018」智力竞赛(二分+DAG最小可相交路径覆盖)

    https://loj.ac/problem/2574

    这个题目描述扎心了。

    简要题意:
    用n+1条可以相交的路径去覆盖DAG,使得没被覆盖的点的权值的最小值最大。

    首先二分答案,问题转换为有一些点一定要被覆盖,问n+1条路径内有没有解。

    这个可以暴力费用流,每个点拆成两个点,(i->i',r=1),如果这个点必选,则费用为inf,否则为0。
    由于每个点还可以被额外经过无限次,所以再连(i->i',r=inf,w=0)
    若有边((i,j)),则(i'->j)连边,每个点都可以作为终点或者起点,所以(S->i,i'->T)连边。
    求总流量不超过n+1时的最大费用,看能不能把inf都选上。

    事实上这是个经典问题:DAG路径覆盖问题
    https://www.cnblogs.com/justPassBy/p/5369930.html

    先Floyd传递闭包,只保留要被覆盖的点,跑dinic即可。

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int inf = 1e9;
    
    const int N = 505;
    
    int n, m;
    int v[N];
    
    int f[N][N];
    
    const int M = N * N * 3;
    
    int fi[M], nt[M], to[M], r[M], tot = 1;
    int S, T;
    
    void link(int x, int y, int z) {
    //	pp("%d %d %d
    ", x, y, z);
    	nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, fi[x] = tot;
    	nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, fi[y] = tot;
    }
    
    void cl() {
    	fo(i, 1, T) fi[i] = 0;
    	tot = 1;
    }
    
    int p[N], p0;
    
    int d[M], d0, dis[M], cur[M];
    
    int bfs() {
    	fo(i, 1, T) dis[i] = inf;
    	d[d0 = 1] = S; dis[S] = 0;
    	for(int i = 1; i <= d0; i ++) {
    		int x = d[i];
    		cur[x] = fi[x];
    		for(int j = fi[x]; j; j = nt[j]) if(r[j] && dis[to[j]] == inf) {
    			dis[to[j]] = dis[x] + 1;
    			d[++ d0] = to[j];
    		}
    	}
    	return dis[T] < inf;
    }
    
    int dfs(int x, int flow) {
    	if(x == T) return flow;
    	int use = 0;
    	for(int i = cur[x]; i; cur[x] = i = nt[i]) if(r[i] && dis[x] + 1 == dis[to[i]]) {
    		int t = dfs(to[i], min(flow - use, r[i]));
    		use += t, r[i] -= t, r[i ^ 1] += t;
    		if(flow == use) return use;
    	}
    	return use;
    }
    
    int pd(int mi) {
    	S = 2 * m + 1, T = S + 1;
    	cl();
    	p0 = 0;
    	fo(i, 1, m) if(v[i] < mi) {
    		p[++ p0] = i;
    		link(S, i, 1);
    		link(i + m, T, 1);
    	}
    	fo(i, 1, p0) fo(j, 1, p0) if(i != j && f[p[i]][p[j]] < inf) {
    		link(p[i], p[j] + m, 1);
    	}
    	int sum = 0;
    	while(bfs()) sum += dfs(S, 1 << 30);
    	sum = p0 - sum;
    	return sum <= n;
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	n ++;
    	fo(i, 1, m) fo(j, 1, m) if(i != j) f[i][j] = 1e9;
    	fo(i, 1, m) {
    		int k, j;
    		scanf("%d %d", &v[i], &k);
    		fo(u, 1, k) {
    			scanf("%d", &j);
    			f[i][j] = 1;
    		}
    	}
    	fo(k, 1, m) fo(i, 1, m) fo(j, 1, m)
    		f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    	int as = 0;
    	for(int l = 0, r = 1e9 + 1; l <= r; ) {
    		int m = l + r >> 1;
    		if(pd(m)) as = m, l = m + 1; else r = m - 1;
    	}
    	if(as > 1e9) pp("AK
    "); else {
    		pp("%d
    ", as);
    	}
    }
    
  • 相关阅读:
    P1030 求先序排列 P1305 新二叉树
    spfa
    Clairewd’s message ekmp
    Cyclic Nacklace hdu3746 kmp 最小循环节
    P1233 木棍加工 dp LIS
    P1052 过河 线性dp 路径压缩
    Best Reward 拓展kmp
    Period kmp
    Substrings kmp
    Count the string kmp
  • 原文地址:https://www.cnblogs.com/coldchair/p/12675852.html
Copyright © 2011-2022 走看看