【题解】 CF786E ALT 网络流+倍增+优化建图

Legend

Link \(\textrm{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)\) 级别!

其实你还可以用倍增的 \(\rm{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\n" ,x ,dep[x]);
	for(int i = 1 ; i <= 14 ; ++i){
		// printf("fa[%d][%d] = %d\n" ,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)\n" ,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\n" ,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\n" ,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\n" ,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;
}
posted @ 2020-09-27 19:35  Imakf  阅读(294)  评论(0编辑  收藏  举报