HDU 4338 Simple Path 点双连通+lca
来源:互联网 发布:中航技国际经贸知乎 编辑:程序博客网 时间:2024/04/28 12:56
【题目大意】
给你一个无向图。问,从点u到点v,若是只走简单路径,有多少个点不能到达?
【思路】
这题肯定是要双连通缩点的,以前老是觉得点双连通不会用来缩点,因为割点可以属于多个连通集合,现在立马打脸了 o(╯□╰)o
最开始用,边-双连通思考,然后发现下面这种图就....
如果我们询问(6,7)和(6,4),显然两次点6所属集团的意义不同;除此之外,比方(1,2,3)这个集团,直接合并也是不合理的,询问(1,3)和(3,7),点3的意义也是不同的
问题的关键,其实关键就是割点。如果对点双连通比较熟悉的话,应该能想到:除了孤点,任何点都会至少属于一个连通集合,而只有割点能属于2个及以上的集合。连通集合一定是通过割点相连的。
所以,我们可以考虑把原图改成通过割点相连的形式。上面那个图就可以改成
显然,每条边的一段都是连通集合,而另一段是割点。
仔细分析割点到割点、非割点到非割点、割点到非割点三种情况,你会发现,点u到点v,能走的点就是修改后的图(这个图不会有环)对应的简单路径上的所有的点。比方(2,5)能经过的点就为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5} == {1,2,3,5} ,(2,6)的为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5}∪{4,5,6} == {1,2,3,4,5,6}。
因为路径上的点是有重复的,我们仍需要思考一下。实际上,点的数目,等于每个点的点的数目减去路径上的边数。
而具体怎么去求无环图上一个简单路径上的点数和以及边数和。这里可以采用多种方法,我这里用的lca维护一下。
P.S. 数据好像不严,询问中有点的编号>=n的情况,我这种情况是直接输出n
#pragma comment(linker, "/STACK:102400000,102400000")#include<cstdio>#include<cstring>#include<vector>#include<queue>#include<cmath>#include<cctype>#include<string>#include<algorithm>#include<iostream>#include<ctime>#include<map>#include<set>using namespace std;#define MP(x,y) make_pair((x),(y))#define PB(x) push_back(x)typedef __int64 LL;//typedef unsigned __int64 ULL;/* ****************** */const LL INF = 1LL<<55;const double INFF = 1e100;const double eps = 1e-8;const LL mod = 10000000007LL;const int NN = 100010;const int MM = 400010;/* ****************** */int dfn[NN], low[NN], tsp;int sta[MM], sta_top;int id[NN], num[NN*2], id_cnt;int color[NN];vector<int>bccno[NN];struct G{ int u, v, next;}E[MM], E1[MM];int p1[NN], T1;int p[NN*2], T;int long2[NN*2*2];int deep[NN*2], pos[NN*2], dian[NN*2];int oula[NN*2*2];int rmq[NN*2*2][20];void init_long2(int n){ int i; long2[1]=0; for(i=2;i<=n;i++) { long2[i]=long2[i-1]+(i==(i&(-i))); }}void add(int u,int v,G *E,int *p,int &T){ E[T].u = u; E[T].v = v; E[T].next = p[u]; p[u] = T++;}void bcc(int u,int fa,int col){ int i, ii, v; dfn[u] = low[u] = ++tsp; color[u] = col; for(i = p1[u]; i + 1; i = E1[i].next) { v = E1[i].v; if(dfn[v]==0) { sta[++sta_top] = i; bcc(v, u, col); low[u] = min(low[u], low[v]); if(low[v] >= dfn[u]) { num[++id_cnt] = 0; for(;;) { ii = sta[sta_top--]; if(id[ E1[ii].u ] != id_cnt) { bccno[ E1[ii].u ].PB(id_cnt); num[id_cnt] ++; id[ E1[ii].u ] = id_cnt; } if(id[ E1[ii].v ] != id_cnt) { bccno[ E1[ii].v ].PB(id_cnt); num[id_cnt] ++; id[ E1[ii].v ] = id_cnt; } if(E1[ii].u == u && E1[ii].v == v) break; } } } else if(dfn[v]<dfn[u] && v != fa) { sta[++sta_top] = i; low[u] = min(low[u], dfn[v]); } }}//生成欧拉序列,计算每个节点深度,每个点首次出现的位置//其第一个父亲,没有用-1表示void dfs(int u,int fa,int cen){ oula[++tsp]=u; deep[u]=cen; pos[u]=tsp; int i,v; for(i=p[u];i+1;i=E[i].next) { v=E[i].v; if(v!=fa) { dian[v] = dian[u] + num[v]; dfs(v,u,cen+1); oula[++tsp]=u; } }}//用于lca的rmqvoid init_rmq(int n){ int i,j,en,len; int t1,t2; for(i=1;i<=n;i++) rmq[i][0]=i; for(j=1;j<=long2[n];j++) { en=n+1-(1<<j); len=1<<(j-1); for(i=1;i<=en;i++) { t1=oula[ rmq[i][j-1] ]; t2=oula[ rmq[i+len][j-1] ]; if(deep[t1]<deep[t2]) rmq[i][j]=rmq[i][j-1]; else rmq[i][j]=rmq[i+len][j-1]; } }}int ask_lca(int u,int v){ int st=pos[u]; int en=pos[v]; if(st>en) swap(st,en); int k=long2[en-st+1]; int id1=oula[ rmq[st][k] ]; int id2=oula[ rmq[en+1-(1<<k)][k] ]; if(deep[id1]<deep[id2]) return id1; return id2;}void solve(int n,int sum_n){ int m,i,u,v,ans; tsp=0; for(i=1;i<=n;i++) pos[i] = -1; for(i=1;i<=n;i++) { if(pos[i]==-1) { dian[i] = num[i]; dfs(i,-1,0); } } init_rmq(tsp); scanf("%d",&m); while(m--) { scanf("%d%d",&u,&v); if(u>=sum_n || v>=sum_n) { printf("%d\n",sum_n); // while(1); continue; } u++, v++; if(u==v) printf("%d\n", sum_n-1); else if(color[u]!=color[v]) printf("%d\n", sum_n); else { u = id[u]; v = id[v]; i = ask_lca(u, v); ans = dian[u] + dian[v] - dian[i]*2 + num[i]; ans -= (deep[u]+deep[v]-deep[i]*2); ans = sum_n - ans; printf("%d\n",ans); } } puts("");}int main(){ init_long2(100000*4); int n, m, i, j, u, v, si; int ee = 0; while(scanf("%d%d", &n, &m) != EOF) { memset(p1, -1, sizeof(p1)); T1 = 0; for(i = 0; i < m; i ++) { scanf("%d%d", &u, &v); u ++, v ++; add(u, v, E1, p1, T1); add(v, u, E1, p1, T1); } memset(dfn, 0, sizeof(dfn)); memset(id, -1, sizeof(id)); tsp = 0; id_cnt = 0; sta_top = 0; for(i = 1; i <= n; i ++) { if(dfn[i]==0) { bcc(i, -1, i); } } memset(p, -1, sizeof(p)); T = 0; for(i = 1; i <= n; i ++) { si = bccno[i].size(); if(si > 1) { u = ++id_cnt; id[i] = u; num[u] = 1; for(j = 0; j < si; j ++) { v = bccno[i][j]; add(u, v, E, p, T); add(v, u, E, p, T); } } bccno[i].clear(); } printf("Case #%d:\n",++ee); solve(id_cnt, n); } return 0;}
- HDU 4338 Simple Path 点双连通+lca
- hdu - 4338 - Simple Path - 割点 && 双连通
- hdu 3686 点双连通 按<割点>缩点 + 倍增lca
- hdu 4338 Simple Path
- 【HDU】2460 Network 双连通+LCA变形
- 【HDU】3686 Traffic Real Time Query System 点双连通+LCA
- hdu 3749 点双连通
- hdu 3394(点双连通)
- hdu 5739(点双连通)
- HDU 5739(点双连通)
- poj 3694 双连通 缩点 桥 LCA
- HDU 3686 Traffic Real Time Query System(点双连通缩点 + LCA)
- HDU 5739 (点双连通 树DP)
- hdu 4674 边双连通缩点+倍增lca+麻烦的讨论 (2013多校联合)
- poj3694--Network(双连通缩点+lca)
- poj3694Network(lca+边双连通缩点)
- hdu 2460(边双连通分量+LCA)
- HDU 5409 双连通缩点
- C语言经典例题及答案3
- 咖啡馆装修需要选择什么样子的地板
- IOS-模糊搜索UISearchBar+UISearchDisplayController
- 简洁优雅的Mac OS X软件安装体验 - homebrew-cask
- C语言经典例题及答案4
- HDU 4338 Simple Path 点双连通+lca
- 转 通用模型之-party model 学习笔记
- [hdu4976]贪心+dp
- android ImageView scaleType属性
- C#,GDI+开发调音器界面
- C语言经典例题及答案5
- android sensors 总结(五)
- Protocol Buffers介绍
- 转 screen widget 框架页面生成原理初探