poj 2793 Cactus(无向仙人掌图判定)

有向Cactus图:

1.它是一个强连通图。

2.它的任意一条边都属于且仅属于一个环。

有向Cactus图判定:

性质有向CactusDFS树没有横向边(不等价于非父子边)

性质2 low(u)<=dfn(v) (uv的儿子)

性质设某个点va(v)个儿子的low值小于dfn(v),同时v自己有b(v)条逆向边。那么a(v)+b(v)<2

这三条性质也就是一个有向图是有向Cactus的充要条件。详细的证明请看《cactus solution》写的很详细,三个条件都有。

对应的题目HDU 3594,只要搞懂了定理很好实现,通过此题深刻理解了横叉边和反向边的区别。


无向Cactus图定义:

1.它是一个连通图。

2.它的任意一条边都至多属于一个环。

poj 2793 Cactus

题意:判断一个图是否为cactus图,并求计算一个图的cactus度:有多少个生成子图(包括自身)也是cactus。

解法:根据无向仙人掌图的定义,只需判断每条边属于几个环即可,对图进行dfs后所有父子边形成以棵树,每条反向边<a,b>加入树后都会使a-lca[a][b]--b形成一个环,可以用poj3417的做法(详见《Tarjan离线算法求LCA小结》)统计每条边被环覆盖了多少次。还有一种方法是在dfs时顺便维护,实现起来可能还会更简单些。

在确定是cactus图后只需统计每个环上有多少条边,然后利用乘法原理就可以计算度数.由于每条反向边只会形成一个环,因此记录每个节点的深度depth[],在碰到反向边<b,a>时,depth[b]-depth[a]+1就是这个环内边的条数,累乘即可,注意要用高精。

public class Main{
	int maxn = 20010, maxm = 1000010;
	class node {
		int be, ne, val;
		node(int b, int e, int v) {
			be = b;
			ne = e;
			val = v;
		}
	}
	class LCA {
		node buf[] = new node[maxn * 2], query[] = new node[maxn * 2];
		int Eb[] = new int[maxn], lb, Eq[] = new int[maxn], lq;
		void init(int n) {
			lq = lb = 0;
			for (int i = 1; i <= n; i++) {
				f[i] = i;
				Eb[i] = Eq[i] = -1;
				vis[i] = 0;
			}
		}
		void addedge(int a, int b, int v) {
			buf[lb] = new node(b, Eb[a], v);
			Eb[a] = lb++;
			buf[lb] = new node(a, Eb[b], v);
			Eb[b] = lb++;
		}
		void addquery(int a, int b, int v) {
			query[lq] = new node(b, Eq[a], v);
			Eq[a] = lq++;
			query[lq] = new node(a, Eq[b], v);
			Eq[b] = lq++;
		}
		int f[] = new int[maxn], vis[] = new int[maxn];
		int find(int x) {
			if (x != f[x])
				f[x] = find(f[x]);
			return f[x];
		}
		void dfs(int a) {
			vis[a] = 1;
			// 处理子树
			for (int i = Eq[a]; i != -1; i = query[i].ne) {
				int b = query[i].be;
				if (vis[b] == 1) {
					int temp = find(b);
					ans[temp] -= 2;
					//System.out.println("te "+temp);
				}
			}
			for (int i = Eb[a]; i != -1; i = buf[i].ne) {
				int b = buf[i].be;
				if (vis[b] == 0) {
					dfs(b);
					f[b] = a;
					ans[a] += ans[b];
				//	System.out.println(a+" "+b+" "+ans[a]+" "+ans[b]);
				}
			}
		}
	}

	class BCC {
		node buf[] = new node[maxm*2];
		int E[] = new int[maxn], len, n;
		int dfn[] = new int[maxn], low[] = new int[maxn],cnt;
		int depth[]=new int[maxn];
		void init(int n) {
			this.n = n;
			Arrays.fill(E, -1);
			len = 0;
		}
		void add(int a, int b) {
			buf[len] = new node(b, E[a], 0);
			E[a] = len++;
			buf[len] = new node(a, E[b], 0);
			E[b] = len++;
		}

		void dfs(int a, int fa,int d) {
			//System.out.println(a);
			dfn[a] = low[a] = ++cnt;
			depth[a]=d;
			for (int i = E[a]; i != -1; i = buf[i].ne) {
				int b = buf[i].be;
				if (dfn[b] == 0) {
					dfs(b, a,d+1);
					lca.addedge(a, b,i);
					low[a] = Math.min(low[b], low[a]);
				} else if (b != fa && dfn[b] < low[a]) {
					low[a] = dfn[b];
					//System.out.println(a+" "+b);
					res=res.multiply(new BigInteger(depth[a]-depth[b]+2+""));
					ans[a]++;
					ans[b]++;
					lca.addquery(a, b, i);
				}
			}
		}
		String solve() {		
			for (int i = 1; i <= n; i++)
				dfn[i] = low[i] = 0;
			cnt = 0;
			Arrays.fill(ans, 0);
			dfs(1,1,1);
			for (int i = 1; i <= n; i++)
				if (dfn[i] == 0)	
					return "0";									
			lca.dfs(1);
			for(int i=1;i<=n;i++)
				if(ans[i]>1)				
					return "0";
					
			return res.toString();
		}
	}

	int ans[] = new int[maxn];
	LCA lca = new LCA();
	BCC bcc = new BCC();
	int path[] = new int[1010];
	boolean vis[] = new boolean[maxn];
	BigInteger res;
	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
			new InputStreamReader(System.in)));

	int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}
	void run() throws IOException {
		int n = nextInt();
		int m = nextInt();
		res=BigInteger.ONE;
		bcc.init(n);
		lca.init(n);
		while (m-- > 0) {
			int k = nextInt();
			for (int i = 1; i <= k; i++)
				path[i] = nextInt();
			for (int i = 1; i < k; i++)
				bcc.add(path[i], path[i + 1]);
		}
		System.out.println(bcc.solve());
	}
	public static void main(String[] args) throws IOException {
		new Main().run();

	}

}

关于cactus 问题,周冬08年的论文里有详细的介绍,主要是利用dfs树性质结合cactus图双连通、强连通的特殊性进行求解,例如求cactus直径/最长链问题等。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值