HDU - 5452 Minimum Cut 树上差分

题目链接:https://cn.vjudge.net/problem/HDU-5452

题意:n个点,m条边,前n-1条构成一棵树,问最少去掉多少遍使n个点不连通,前n-1条只能去一个

题解:对于一棵树,加上一条边,会形成对应的环,那么这个环上的边要想去掉的话,刚加上的那条边也要去掉,也就是说如果去掉这条边,所对应的最小边集的数量要加一,那么把n-1条边分配给点即可,这样直接思路就是树剖了,但是超时,所以可以用差分来做,我直接在原先的树剖的代码上改的,要注意的一点是,树上差分是lca减,两个节点加,然后倒着回去,为啥呢,因为节点的父亲只有一个,而孩子可能有很多。

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 20010;
struct edge {
	int to, nex;
}e[N * 10 * 2];
int head[N], len;
int son[N], num[N], deep[N], fa[N], top[N];
int n, m;
int vis[N], in[N];
void init() {
	for(int i = 1; i <= n; i++) {
		head[i] = -1;
		son[i] = -1;
		num[i] = 1;
		fa[i] = 0;
		top[i] = 0;
		vis[i] = 0;
		in[i] = 0;
	}
	len = 0;
}
void add_edge(int x, int y) {
	e[len].to = y;
	e[len].nex = head[x];
	head[x] = len++;
}
void dfs1(int u, int f) {
	deep[u] = deep[f] + 1;
	fa[u] = f;
	int to;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == f) continue;
		dfs1(to, u);
		num[u] += num[to];
		if(son[u] == -1 || num[to] > num[son[u]]) {
			son[u] = to;
		}
	}
}
void dfs2(int u, int f, int rt) {
	top[u] = rt;
	if(son[u] != -1) {
		dfs2(son[u], u, rt);
	}
	int to;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == son[u] || to == f) continue;
		dfs2(to, u, to);
	}
}
void solve(int x, int y) {
//	cout<<x<<" "<<y<<" "<<top[x]<<" "<<top[y]<<endl;
	int fx = top[x], fy = top[y];
	while(fx != fy) {
		if(deep[fx] < deep[fy]) {
			swap(fx, fy);
			swap(x, y);
		}
		x = fa[x];
		fx = top[x];
	}
	if(deep[x] > deep[y]) swap(x, y);
	vis[x]-=2;
}
int ans;
void query(int u, int f ) {
	int to;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == f) continue;
		query(to, u);
		vis[u] += vis[to];
	}
	if(u != 1)
	ans = min(ans, vis[u]);
}
int main() {
	int T, ca = 1;
	int x, y, z;
	scanf("%d", &T);
	while(T--) {
		scanf("%d %d", &n, &m);
		init();
		for(int i = 1; i < n; i++) {
			scanf("%d %d", &x, &y);
			add_edge(x, y);
			add_edge(y, x);
		}
		dfs1(1, 0);
		dfs2(1, 0, 1); 
		for(int i = n; i <= m; i++) {
			scanf("%d %d", &x, &y);
			
			solve(x, y);
			vis[x]++;
			vis[y]++;
		}
		ans = INF;
		query(1, 0);
		printf("Case #%d: %d\n", ca++, ans + 1);
	}
	return 0;
}

树剖T的代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 20010;
struct node {
	int l, r;
	int laz;
}tree[N << 2];
struct edge {
	int to, nex;
}e[N * 10 * 2];
int head[N], len;
int son[N], num[N], deep[N], fa[N], top[N];
int in[N], cnt;
int n, m;
void build(int l, int r, int cur) {
	tree[cur].l = l;
	tree[cur].r = r;
	tree[cur].laz = 0;
	if(l == r) {
		tree[cur].laz = 1;
		return;
	}
	int mid = (r + l) >> 1;
	build(l, mid, cur << 1);
	build(mid + 1, r, cur << 1 | 1);
}
void init() {
	for(int i = 1; i <= n; i++) {
		head[i] = -1;
		son[i] = -1;
		num[i] = 1;
		fa[i] = 0;
		top[i] = 0;
	}
	len = 0;
	cnt = 0;
	build(1, n, 1);
}
void add_edge(int x, int y) {
	e[len].to = y;
	e[len].nex = head[x];
	head[x] = len++;
}
void dfs1(int u, int f) {
	deep[u] = deep[f] + 1;
	fa[u] = f;
	int to;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == f) continue;
		dfs1(to, u);
		num[u] += num[to];
		if(son[u] == -1 || num[to] > num[son[u]]) {
			son[u] = to;
		}
	}
//	cout<<u<<" - "<<son[u]<<endl;
}
void dfs2(int u, int f, int rt) {
	in[u] = ++cnt;
	top[u] = rt;
	if(son[u] != -1) {
		dfs2(son[u], u, rt);
	}
	int to;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == son[u] || to == f) continue;
		dfs2(to, u, to);
	}
}
void update(int pl, int pr, int cur) {
	if(pl <= tree[cur].l && tree[cur].r <= pr) {
		tree[cur].laz += 1;
		return;
	}
	if(pl <= tree[cur << 1].r) update(pl, pr, cur << 1);
	if(pr >= tree[cur << 1 | 1].l) update(pl, pr, cur << 1 | 1);
}
void solve(int x, int y) {

	int fx = top[x], fy = top[y];
//	cout<<fx<<" "<<fy<<endl;
	while(fx != fy) {
		if(deep[fx] < deep[fy]) {
			swap(fx, fy);
			swap(x, y);
		}
		update(in[fx], in[x], 1);
		x = fa[x];
		fx = top[x];
	//	cout<<x<<" "<<y<<endl;
	}
	if(deep[x] > deep[y]) swap(x, y);
	if(x != y) update(in[x] + 1, in[y], 1);
}
int ans;
void pushdown(int cur) {
	if(tree[cur].laz) {
		tree[cur << 1].laz += tree[cur].laz;
		tree[cur << 1 | 1].laz += tree[cur].laz;
		tree[cur].laz = 0; 
	}
}
void query(int cur) {
	if(tree[cur].l == tree[cur].r) {
		if(tree[cur].l == 1) return;
		ans = min(ans, tree[cur].laz);
		return;
	}
	pushdown(cur);
	query(cur << 1);
	query(cur << 1 | 1);
}
int main() {
	int T, ca = 1;
	int x, y, z;
	scanf("%d", &T);
	while(T--) {
		scanf("%d %d", &n, &m);
		init();
		for(int i = 1; i < n; i++) {
			scanf("%d %d", &x, &y);
			add_edge(x, y);
			add_edge(y, x);
		}
		dfs1(1, 0);
		dfs2(1, 0, 1); 
		for(int i = n; i <= m; i++) {
			scanf("%d %d", &x, &y);
			solve(x, y);
		}
		ans = INF;
		query(1);
		printf("Case #%d: %d\n", ca++, ans);
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值