VIJOS-P1325 桐桐的糖果计划
JDOJ 1432 桐桐的糖果计划
https://neooj.com/oldoj/problem.php?id=1432
Description
Input
Output
Sample Input
Sample Output
void tarjan(int x)//tarjan算法模板(链式前向星) { z[++top]=x; v[x]=1; inz[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(v[y]==0) tarjan(to[i]),low[x]=min(low[x],low[to[i]); else if(inz[to[i]]==1) low[x]=min(low[x],deep[to[i]]); } if(deep[x]==low[x]) { ans++; int t; do { t=z[top--]; inz[t]=0; f[ans][++f[ans][0]]=t; }while(t!=x) } }
说明一下,deep数组表示时间戳,即节点x被搜索的次序编号。
low数组表示x能通过其他边回到的x的祖先,标记的祖先最小的时间戳。
深搜回溯到当前节点之后,当deep[x]==low[x],以x为根的搜索子树上的所有节点为一个强连通分量。
z数组模拟数据结构栈,inz数组表示元素是否在栈中,v数组表示元素是否被搜索过。
f数组记录每一个强连通分量里元素的个数和所有元素。
好了,下面我们介绍用tarjan算法求割边。
假如low[y]>deep[x]的时候,说明y不仅不能到达x的祖先,也不能通过另外一条边直接直接直接到达x,说明它们之间的e(x,y)便是割边。
注意处理重边。
处理重边的方式有两种,依据重边有用没用而定(而有用没用依题目而定)(等于没说)
假如重边没有用,在tarjan的时候加一个参数记录它的父亲,注意是参数,当y遇到父亲节点时不拓展回去,保证重边不会被遍历。
假如重边有用,那么增加一个参数记录边的编号,y不能通过这条边访问其父亲节点,但是却可以通过重边访问x,保证重边会被遍历到。
那么好了,我们如何来判定重边?
这里要引入对偶边的判定。
位运算异或,请小伙伴们自行补习。
void tarjan(int x,int pre) { v[x]=true; deep[x]=low[x]=++tot; for(int i=head[x];i!=-1;i=next[i]) { int y=to[i]; if( (i^1)==pre ) continue; if(!deep[y]) { tarjan(y,i); low[x]=min(low[x],low[y]); if(low[y]>deep[x]) //割边; } else low[x]=min(low[x],deep[y]); } }
好了我们来针对一下这道题,第一问通过刚才的讲解已经没什么难度了。
来看第二问,这里注意我们是求缩点后需要加多少条边。
这里我们需要画图理解。
缩点后的无向图会变成一棵树,统计度为2的那些双联通分量就是叶子结点(双向边)。
然后ans=(t+1)/2;
好了讲了这么多相信大家也会对这个有一定的理解,细节的实现可以自己慢慢调。下面的代码仅供参考
#include<iostream> #include<cstdio> #include<cstring> #define maxn 10010 using namespace std; int n,m,num,head[maxn],low[maxn],deep[maxn],topt; int top,z[maxn],inz[maxn],ans,sum,belong[maxn],rudu[maxn]; struct node { int v,pre; }e[maxn*2]; void add(int from,int to) { e[num].v=to; e[num].pre=head[from]; head[from]=num++; } void tarjan(int x,int fa) { low[x]=deep[x]=++topt; z[++top]=x;inz[x]=1; for(int i=head[x];i!=-1;i=e[i].pre) { int v=e[i].v; if(i==(fa^1)) continue; if(deep[v]==0) { tarjan(v,i); low[x]=min(low[x],low[v]); if(low[v]>deep[x])ans++; } else if(inz[v]) low[x]=min(low[x],deep[v]); } if(low[x]==deep[x]) { sum++; while(x!=z[top]) { belong[z[top]]=sum; inz[z[top]]=0; top--; } belong[z[top]]=sum; inz[z[top]]=0; top--; } } int main() { scanf("%d%d",&n,&m); int u,v; memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(int i=1;i<=n;i++) if(deep[i]==0) tarjan(i,-1); printf("%d\n",ans); ans=0; for(int u=1;u<=n;u++) for(int i=head[u];i!=-1;i=e[i].pre) { int v=e[i].v; if(belong[u]!=belong[v]) rudu[belong[u]]++; } for(int i=1;i<=sum;i++) if(rudu[i]==1) ans++; printf("%d\n",(ans+1)/2); }