zoukankan      html  css  js  c++  java
  • 【题解】 CF786E ALT 网络流+倍增+优化建图

    Legend

    Link ( extrm{to Codeforces})

    给定 (n (2 le n le 20000)) 个点的树,以及 (m (1 le m le 10000)) 个人的行走路线(一条简单路径),你需要使得所有人都满意。一个人满意当且仅当:

    • ta 的行走路线的所有边上都有一只狗。
    • 直接送给 ta 一只狗。

    求最少需要多少只狗。输出方案。

    Editorial

    不妨考虑序列问题怎么做?对于一个人的行走路线,要么给区间全放上狗,要么直接给他一只狗。

    人、区间—— (2) 个不同的选择——不禁让人想到了二分图。于是建模很快完成了:

    人当作左部图,区间的每一个位置是右部图,源点连人,位置连汇点,边权 (1)

    人向经过的区间的位置一个一个连过去,边权 (infty)

    最后最小割就是答案了。

    这个做法也很容易还原到树上,但唯一的不足就是边太多了,达到了 (O(nm)) 级别。

    考虑优化建图,序列上可以线段树,树上就可以树链剖分,一下子就降到了 (O(m log^2 n)) 级别!

    其实你还可以用倍增的 ( m{ST}) 表优化建图,就变成了 (O(m log n)) 级别啦。

    这看起来是个伪·二分图,位置的一侧有 (O(log n)) 层。

    于是我们就不妨假设这个做法的复杂度是 (O(sqrt{n log n} m log n)) 啦!

    其实还有个问题,怎么输出方案?显然在残余网络上,对于一个人,如果你到不了 ta,那就是你给了 ta 一只狗。

    对于一个位置,如果你可以到它,那就是你放了一条狗上去。

    Code

    今日自闭:写倍增的时候先更新的深度大的(在回溯的时候更新的)。

    这么写的话,显然由于上面的还没有被更新,所以下面的更新都没用。。。。

    于是倍增数组除了能访问 parent 节点以外,其它位置都是 (0)。TAT。

    #include <bits/stdc++.h>
    
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    
    using namespace std;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    const int MX = 30000 + 23;
    const int S = MX * 15 + 1;
    const int T = MX * 15 + 2;
    const int vMX = MX * 16;
    const int INF = 1000000;
    
    int lg2[MX] ,n ,m;
    
    int tr[vMX] ,fl[vMX] ,tot = 1; 
    struct edge{
    	int node ,next ,w;
    }h[MX * 60];
    void addedge(int u ,int v ,int w ,int *head ,int flg = false){
    	h[++tot] = (edge){v ,head[u] ,w} ,head[u] = tot;
    	if(flg) addedge(v ,u ,0 ,head ,false);
    }
    
    int fa[16][MX] ,dep[MX];
    int eid[16][MX] ,ecnt;
    void dfs(int x ,int *head){
    	if(!dep[x]) dep[x] = 1;
    	// fprintf(stderr ,"dep[%d] = %d
    " ,x ,dep[x]);
    	for(int i = 1 ; i <= 14 ; ++i){
    		// printf("fa[%d][%d] = %d
    " ,i - 1 ,x ,fa[i - 1][x]);
    		if(!fa[i - 1][fa[i - 1][x]]) break;
    		fa[i][x] = fa[i - 1][fa[i - 1][x]];
    		eid[i][x] = ++ecnt;
    		// printf("E(%d) is for chain (%d ,%d)
    " ,ecnt ,x ,fa[i - 1][fa[i - 1][x]]);
    		addedge(eid[i][x] ,eid[i - 1][x] ,INF ,fl ,1);
    		addedge(eid[i][x] ,eid[i - 1][fa[i - 1][x]] ,INF ,fl ,1);
    	}
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if((d = h[i].node) == fa[0][x])  continue;
    		fa[0][d] = x ,eid[0][d] = h[i].w;
    		dep[d] = dep[x] + 1;
    		dfs(d ,head);
    	}
    }
    
    int LCA(int x ,int y){
    	if(dep[x] < dep[y]) swap(x ,y);
    	for(int i = 14 ; ~i ; --i)
    		if(dep[fa[i][x]] >= dep[y])
    			x = fa[i][x];
    	if(x == y) return x;
    	for(int i = 14 ; ~i ; --i)
    		if(fa[i][x] != fa[i][y])
    			x = fa[i][x] ,y = fa[i][y];
    	return fa[0][x];
    }
    
    int jump(int x ,int dist){
    	for(int i = 14 ; ~i ; --i)
    		if((dist >> i) & 1)
    			x = fa[i][x];
    	return x;
    }
    
    void coverchain(int x ,int y ,int id){
    	if(dep[x] < dep[y]) swap(x ,y);
    	// fprintf(stderr ,"sp %d %d
    " ,x ,y);
    	// assert(x != y);
    	int dist = dep[x] - dep[y];
    	int len = lg2[dist];
    	// assert(len >= 0);
    	addedge(id ,eid[len][x] ,INF ,fl ,1);
    	int tmp = jump(x ,dist - (1 << len));
    	addedge(id ,eid[len][tmp] ,INF ,fl ,1);
    }
    
    void cover(int x ,int y ,int id){
    	if(dep[x] < dep[y]) swap(x ,y);
    	int lca = LCA(x ,y);
    	if(lca == y){
    		return coverchain(x ,y ,id);
    	}
    	coverchain(x ,lca ,id);
    	coverchain(y ,lca ,id);
    }
    
    
    void init(){
    	lg2[0] = -1;
    	for(int i = 1 ; i < MX ; ++i){
    		lg2[i] = lg2[i - 1] + (i == (i & -i));
    	}
    	ecnt = n - 1 + m;	
    	dfs(1 ,tr);
    }
    
    int dis[vMX] ,cur[vMX];
    bool BFS(int *head){
    	for(int i = 1 ; i <= ecnt ; ++i){
    		cur[i] = head[i];
    		dis[i] = 0;
    	}
    	for(auto i : {S ,T}){
    		cur[i] = head[i];
    		dis[i] = 0;
    	}
    	queue<int> q; q.push(S);
    	dis[S] = 1;
    	while(!q.empty()){int x = q.front(); q.pop();
    		for(int i = head[x] ,d ; i ; i = h[i].next){
    			if(!dis[d = h[i].node] && h[i].w){
    				dis[d] = dis[x] + 1;
    				q.push(d);
    			}
    		}
    	}return dis[T];
    }
    
    int DFS(int x ,int limit){
    	if(x == T || !limit) return limit;
    	int f ,flow = 0;
    	for(int i = cur[x] ,d ; i && limit ; i = h[i].next){
    		cur[x] = i ,d = h[i].node;
    		if(dis[d] == dis[x] + 1 && (f = DFS(d ,min(limit ,h[i].w)))){
    			flow += f ,h[i ^ 1].w += f;
    			h[i].w -= f ,limit -= f;
    		}
    	}return flow;
    }
    
    void prepareAns(int x ,int *head){
    	dis[x] = 1;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if((dis[d = h[i].node]) || !h[i].w) continue;
    		prepareAns(d ,head);
    	}
    }
    
    void test(){
    	while(1){
    		printf("%d
    " ,read());
    	}
    }
    
    int main(){
    	__FILE(CF786E);
    	n = read() ,m = read();
    	
    	for(int i = 1 ,u ,v ; i < n ; ++i){
    		u = read() ,v = read();
    		addedge(u ,v ,i ,tr);
    		addedge(v ,u ,i ,tr);
    	}
    	
    	for(int i = 1 ; i < n ; ++i){
    		addedge(i ,T ,1 ,fl ,true);
    	}
    	for(int i = 1 ; i <= m ; ++i){
    		addedge(S ,i + n - 1 ,1 ,fl ,true);
    	}
    
    	
    	init();	
    
    	for(int i = 1 ,u ,v ; i <= m ; ++i){
    		u = read() ,v = read();
    		cover(u ,v ,i + n - 1);
    	}
    	int orgAns = 0;
    	while(BFS(fl)){
    		orgAns += DFS(S ,INF);
    	}
    	printf("%d
    " ,orgAns);
    	
    	memset(dis ,0 ,sizeof dis);
    	prepareAns(S ,fl);
    	int Ans = 0;
    	for(int i = 1 ; i <= m ; ++i){
    		if(!dis[i + n - 1]){
    			++Ans;
    		}
    	}
    	printf("%d " ,Ans);
    	for(int i = 1 ; i <= m ; ++i){
    		if(!dis[i + n - 1]){
    			printf("%d " ,i);
    		}
    	}
    	puts("");
    	printf("%d " ,orgAns - Ans);
    	for(int i = 1 ; i < n ; ++i){
    		if(dis[i]){
    			printf("%d " ,i);
    		}
    	}
    
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    洛谷 1850 NOIP2016提高组 换教室
    2018牛客多校第三场 C.Shuffle Cards
    2018牛客多校第一场 B.Symmetric Matrix
    2018牛客多校第一场 A.Monotonic Matrix
    2018牛客多校第一场 D.Two Graphs
    2018宁夏邀请赛L Continuous Intervals
    2018宁夏邀请赛K Vertex Covers
    BZOJ
    HDU
    ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2015)
  • 原文地址:https://www.cnblogs.com/imakf/p/13741407.html
Copyright © 2011-2022 走看看