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;
    }
    
  • 相关阅读:
    电路开发软件积累
    .NET移动开发环境搭建
    Microsoft SQL Server Express各版本对比
    .NET IL指令速查表
    C#访问加密的SQLite数据库
    C#中时间计算方法汇总
    最佳C/C++编辑器 source insight3
    常用PHP框架收集
    CSS 中文字体的英文名称 (simhei, simsun) 宋体 微软雅黑等
    Windows下Java JDK8配置环境变量
  • 原文地址:https://www.cnblogs.com/imakf/p/13741407.html
Copyright © 2011-2022 走看看