Redundant Edges(支配树)

题意描述:

给一个N个点,M条边的有向图,和起点R,定义一条边为多余边,满足:起点R能够不经过该边到达所有N个点。

问哪些边是多余的,输出多余边的条数,并按顺序输出所有多余的边

1 <= N <= 100000 , 1 <= M <= 200000

输入:

4 6 2
2 1
2 3
3 1
3 4
1 4
4 2

输出:

5
1 3 4 5 6

 

解题思路:

将边变成点,如果某条边的点是某个点的支配点,那么这条边不能被删除,既这条边不是多余的边。

剩下的边就都是多余的边了。支配树模板搞一搞。

代码:

#include<bits/stdc++.h>
using namespace std;

/*********支配树模板**********/
//init(N) -> link(u,v) -> work(root) -> idom[i]:root到距离i最近的必经点
const int N = 300010, M = 400010;
struct eg{ int v, next; }e[M];	//link times
int tim, tot, h[N], iw[N], li[N], fa[N], fo[N], vo[N], sdom[N], idom[N];
vector<int> pre[N], bkt[N];
inline void link(int u, int v){
	e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
	pre[v].push_back(u);
}
void dfs(int p){
	sdom[p] = iw[p] = ++tim, li[tim] = p;
	for (int i = h[p]; i; i = e[i].next)
		if (!iw[e[i].v]) dfs(e[i].v), fa[e[i].v] = p;
}
int findf(int p){
	if (fo[p] == p) return p;
	int r = findf(fo[p]);
	if (sdom[vo[fo[p]]] < sdom[vo[p]]) vo[p] = vo[fo[p]];
	return fo[p] = r;
}
inline int eval(int p){ findf(p); return vo[p]; }
inline void work(int root){
	int p, fp;  dfs(root);
	for (int i = tim; i >= 2; i--){
		p = li[i];
		for (int &k : pre[p])
			if (iw[k]) sdom[p] = min(sdom[p], sdom[eval(k)]);
		bkt[li[sdom[p]]].push_back(p);
		fp = fo[p] = fa[p];
		for (int v : bkt[fp]){
			int u = eval(v);
			idom[v] = sdom[u] == sdom[v] ? fp : u;
		}
		bkt[fp].clear();
	}
	for (int i = 2; i <= tim; i++)
        p = li[i], idom[p] = idom[p] == li[sdom[p]] ? idom[p] : idom[idom[p]];
	for (int i = 2; i <= tim; i++)
        p = li[i], sdom[p] = li[sdom[p]];
}
void init(int n){	
	tim = tot = 0;i
	for (int i = 1; i <= n; i++)
        h[i] = iw[i] = 0, fo[i] = vo[i] = i, pre[i].clear(), bkt[i].clear();
}
/*******************/

int n, m, rt, a, b;
bool vis[M];
set<int> s;
int main(){
	scanf("%d %d %d", &n, &m, &rt);
	init(n + m);
	for (int i = 1; i <= m; i++)
		scanf("%d %d", &a, &b), link(a, i + n), link(i + n, b);
	work(rt);
	for (int i = 1; i <= n; i++) if (idom[i] > n) vis[idom[i] - n] = 1;
	for (int i = 1; i <= m; i++) if (!vis[i]) s.insert(i);
	printf("%d\n", s.size());
	for (auto &k : s) printf("%d%c", k, k == *s.rbegin() ? '\n' : ' ');
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值