zoukankan      html  css  js  c++  java
  • (广义)圆方树

    圆方树用于解决仙人掌(没有一条边在两个环上的无向连通图)上的一些问题,广义圆方树用于解决无向图中的一些问题。(不过目前没怎么做过题,不知道广义不广义有啥区别)

    博客

    前置技能

    tarjan 算法求点双

    一定要先学好 tarjan 算法啊!! 吃够了基础不牢的苦

    圆方树的构建

    和 tarjan 点双缩点不一样的是,我们并不去“复制”割点,甚至都不用显式地标出割点,我们直接用一个“方点”代表一个DCC,“方点”向DCC上的所有点连边,以保证每个点只出现一次。特别的,当DCC中只有两个点的时候,我们更倾向于不建“方点”,因为这种情况不会出环,只是个没有割点的DCC

    至于边权,就可能是具体问题具体分析了。(因为我目前就做过一道题)。比如P5236 【模板】静态仙人掌
    这道例题,求仙人掌上两点最短路,我们的处理方法是:圆圆边(圆点-圆点)边权就是原图边权;圆方边边权是圆点到方点的父亲(即DCC里面靠上的那个割点)在原图中的最短距离。查询的话用树剖lca的方法解决,如果lca是圆点,就照常做;如果是方点,就要把经过那个环的两种路径都考虑一下。

    总之,细节很多,需要一些技巧。不过如果不考虑边权的话,圆方树还是比较友好的。

    代码:

    void tarjan(int cur) {
    	dfn[cur] = low[cur] = ++dcnt;
    	stk[++stop] = cur;
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to;
    		if (!dfn[to]) {
    			pre_val[to] = e[i].val;
    			tarjan(to);
    			MIN(low[cur], low[to]);
    			if (low[to] >= dfn[cur]) {
    				if (stk[stop - 1] == cur) {
    					--stop;
    					Addedge(cur, to, e[i].val);
    					Addedge(to, cur, e[i].val);
    					continue;
    				}
    				++dtot;
    				int p = stk[stop], S = lval[p];
    				for (register int j = stop; stk[j] != cur; --j)
    					sum[stk[j]] = S, S += pre_val[stk[j]];
    				while (stk[stop] != cur) {
    					int tmp = stk[stop--];
    					int evl = min(sum[tmp], S - sum[tmp]);
    					Addedge(tmp, dtot, evl);
    					Addedge(dtot, tmp, evl);
    				}
    				Addedge(cur, dtot, 0), Addedge(dtot, cur, 0); sum[cur] = 0;
    				sum[dtot] = S;
    			}
    		} else {
    			if (dfn[to] < low[cur])	low[cur] = dfn[to], lval[cur] = e[i].val;
    		}
    	}
    }
    int Find(int x, int lca) {//找到 x - lca 路径中lca下面的那个点的编号
    	int lst = 0;
    	while (top[x] != top[lca])	lst = top[x], x = fa[top[x]];
    	return x == lca ? lst : son[lca];
    }
    
    ...
    
    int main() {
          ...      
                    int lca = get_lca(u, v);
    		if (lca <= n)
    			printf("%d
    ", dis[u] + dis[v] - (dis[lca] << 1));
    		else {
    			int ans = dis[u] + dis[v];
    			u = Find(u, lca), v = Find(v, lca);
    			ans = ans - dis[u] - dis[v];
    			if (sum[u] < sum[v])	swap(u, v);
    			ans += min(sum[u] - sum[v], sum[lca] - (sum[u] - sum[v]));
    			printf("%d
    ", ans);
    		}
          ...
    }
    

    待续

  • 相关阅读:
    windows7管理员权限
    Linux编程 第一个Hello World的C程序
    初步接触XCode和IPhone Simulator
    Android调用WCF
    mysql常用命令
    怎样利用Python制作守护进程
    Android模拟器如何设置DNS访问局域网内网站
    ubuntu下mysql攻略
    IIS7快速启动
    内存出错有时候是free后没有给指针赋NULL
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13269737.html
Copyright © 2011-2022 走看看