基础知识
例题
POJ 2186 Popular Cows
http://poj.org/problem?id=2186
Tarjan+缩点
建立有向边指向被认为受欢迎的牛。
将Tarjan求出的强连通分量缩点,统计各点出度,若有且只有1个出度为0的,答案即为出度为0的点内包含点数,否则输出0。
缩点之后整张图是 DAG(有向无环图),如果有强连通分量被孤立,出度为0的点数大于1。如果没有强连通分量被孤立, 若有大于1个出度为0的点(相当于路在其中某个出度为0的点处截断),不存在1个点能够被所有点到达。
POJ 1236 Network of Schools
http://poj.org/problem?id=1236
Tarjan+缩点
对于任务B,考虑所有入度或出度为0的点(
ai
,
bi
),将这些点按照一定顺序连接起来,最终添加Max{
Sa
,
Sb
}条边。
注意在只存在一个连通分量时需要特判。
POJ 3592 Instantaneous Transference
http://poj.org/problem?id=3592
Tarjan缩点+SPFA求最长路
UVa 10510 Cactus
原网站比较慢,丢Vjudge链接好了。
https://cn.vjudge.net/problem/UVA-10510
根据仙人掌图的定义,边被两个环同时经过即一个非连接点被经过两次该图即不是仙人掌。Tarjan算法中找到后向边即找到一个环,从当前点向前遍历环打标记。
在写这道题的过程中,小伙伴还发现算法中的一个疑点,为什么给Low[]取min时为什么有的是Low[u]=Min(Low[v],Low[u])而有的是Low[u]=Min(DNF[v],Low[u])呢?在尝试过将所有Low[u]=Min(DNF[v],Low[u])都改为前者之后AC代码们安然无恙之后,我们找到某博客中的一句话,在取Low[u]=Min(DNF[v],Low[u])时,只是为了让算法能够辨别这还不是一个强连通分量的根节点。
POJ 1904 King’s Quest
http://poj.org/problem?id=1904
Tarjan算法
王子向所有喜欢的公主连边,公主向匹配的王子连边 每个王子的答案即为同一个强连通分量中他喜欢的公主。
因为若一个王子a1能够找到一个非原配公主b1,那么b1的原配王子也要找到一个非原配公主b2,……最终总有一个王子找到a1的原配公主。然后这就是一个美妙的环了。
———代码们———
POJ 2186 Popular Cows
#include<vector>
#include<cstdio>
#include<algorithm>
using namespace std;
const int sm = 1e4+10;
const int sn = 5e4+10;
int N,M,S,Bcnt,tot,top,ans,Ct,u,v;
int Bn[sm],Low[sm],Stk[sm],instk[sm];
int to[sn],nxt[sn],hd[sm],Blg[sm];
vector<int> Vc[sm];
void read(int &x) {
char ch=getchar();x=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Add(int u,int v) {
to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
void Tarjan(int x) {
int j;
Bn[x]=Low[x]=++tot;
Stk[++top]=x,instk[x]=true;
for(int i=hd[x];i;i=nxt[i]) {
j=to[i];
if(!Bn[j]) {
Tarjan(j);
Low[x]=Min(Low[x],Low[j]);
} else if(instk[j]&&Low[j]<Low[x])
Low[x]=Low[j];
}
if(Low[x]==Bn[x]) {
++Bcnt;
do {
j=Stk[top--];
instk[j]=false;
Blg[j]=Bcnt;
Vc[Bcnt].push_back(j);
} while(j!=x);
}
}
int main() {
read(N),read(M);
for(int i=1;i<=M;++i)
read(u),read(v),Add(u,v);
for(int i=1;i<=N;++i)
if(!Bn[i]) Tarjan(i);
for(int i=1,Cd;i<=Bcnt;++i) {
v=Vc[i].size(),Cd=0;
for(int j=0;j<v;++j)
for(int k=hd[Vc[i][j]];k;k=nxt[k])
if(Blg[to[k]]!=i) ++Cd;
if(Cd==0) ++S,ans=v;
}
if(S==1) printf("%d\n",ans);
else puts("0");
return 0;
}
POJ 1236 Network of Schools
#include<vector>
#include<cstdio>
using namespace std;
const int sm = 100+5;
const int sn = 10000+5;
int N,ans,Sc,Cd,Bcnt,tot,top,Ct,Rd[sm];
int Bn[sm],Lw[sm],Stk[sm],instk[sm];
int to[sn],nxt[sn],hd[sm],Blg[sm];
int lt[sm][sm];
vector<int>Vc[sm];
void Add(int u,int v) {
to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
int Max(int x,int y) { return x>y?x:y; }
void Tarjan(int x) {
int j;
Bn[x]=Lw[x]=++tot;
instk[x]=true,Stk[++top]=x;
for(int i=hd[x];i;i=nxt[i]) {
j=to[i];
if(!Bn[j]) {
Tarjan(j);
Lw[x]=Min(Lw[x],Lw[j]);
} else if(instk[j]&&Lw[j]<Lw[x])
Lw[x]=Lw[j];
}
if(Bn[x]==Lw[x]) {
++Bcnt;
do {
j=Stk[top--];
instk[j]=false;
Blg[j]=Bcnt;
Vc[Bcnt].push_back(j);
} while(j!=x);
}
}
int main() {
scanf("%d",&N);
for(int i=1,x;i<=N;++i)
while(scanf("%d",&x)==1)
if(!x) break; else Add(i,x);
for(int i=1;i<=N;++i)
if(!Bn[i]) Tarjan(i);
for(int i=1,sz;i<=Bcnt;++i) {
sz=Vc[i].size(),Cd=0;
for(int j=0;j<sz;++j)
for(int k=hd[Vc[i][j]];k;k=nxt[k])
if(Blg[to[k]]!=i) {
++Rd[Blg[to[k]]],++Cd;
if(!lt[i][Blg[to[k]]])
lt[i][Blg[to[k]]]=1;
}
if(!Cd) ++Sc;
}
for(int i=1;i<=Bcnt;++i)
if(!Rd[i]) ++ans;
printf("%d\n%d\n",ans,(Bcnt!=1)?Max(ans,Sc):0);
return 0;
}
POJ 3592 Instantaneous Transference
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sm = 1600+5;
const int sn = sm*3;
int T,N,M,K,top,Ct,St,Bcnt,tot,ans;
int Bn[sm],Lw[sm],Stk[sm],instk[sm],C[sm];
int fr[sn],to[sn],nxt[sn],hd[sm],Blg[sm],Val[sm],dis[sm],Que[sm];
bool vis[sm];
char Ch[45][45],ch;
vector<int> Sd[sm];
void read(int &x) {
char ch=getchar();x=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void init() {
K=Ct=ans=Bcnt=tot=0;
memset(hd,0,sizeof(hd));
memset(Bn,0,sizeof(Bn));
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
}
void Add(int u,int v) {
fr[++Ct]=u,to[Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int a,int b) { return a<b?a:b; }
void Tarjan(int x) {
int j;
Bn[x]=Lw[x]=++tot;
Stk[++top]=x,instk[x]=true;
for(int i=hd[x];i;i=nxt[i]) {
j=to[i];
if(!Bn[j]) {
Tarjan(j);
Lw[x]=Min(Lw[x],Lw[j]);
} else if(instk[j]&&Lw[j]<Lw[x])
Lw[x]=Lw[j];
}
if(Bn[x]==Lw[x]) {
++Bcnt,Val[Bcnt]=0;
do {
j=Stk[top--];
instk[j]=false;
Blg[j]=Bcnt;
Val[Bcnt]+=C[j];
if(j==1) St=Bcnt;
} while(j!=x);
}
}
void SPFA() {
int tail=0,head=0,k,t;
Que[++tail]=St,vis[St]=true;
dis[St]=Val[St];
while(head<tail) {
k=Que[++head],vis[k]=false;
int len=Sd[k].size();
for(int i=0;i<len;++i) {
t=Sd[k][i];
if(dis[t]<dis[k]+Val[t]) {
dis[t]=dis[k]+Val[t];
if(!vis[t])vis[t]=true,Que[++tail]=t;
}
}
}
}
int main() {
read(T);
while(T--) {
init();
read(N),read(M);
for(int i=1;i<=N;++i,ch=getchar())
for(int j=1;j<=M;++j)
scanf("%c",&Ch[i][j]);
int k;
for(int i=1,cnt=0,u,v;i<=N;++i)
for(int j=1;j<=M;++j) {
k=(i-1)*M+j,C[k]=0;
if(Ch[i][j]!='#') {
if(Ch[i+1][j]!='#'&&i+1<=N) Add(k,k+M);
if(Ch[i][j+1]!='#'&&j+1<=M) Add(k,k+1);
if(Ch[i][j]=='*') {
read(u),read(v);
if(Ch[++u][++v]!='#')
Add(k,(u-1)*M+v);
}
else C[k]=Ch[i][j]-'0';
}
}
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j) {
k=(i-1)*M+j;
if(!Bn[k]&&Ch[i][j]!='#')
Tarjan(k);
}
for(int i=1;i<=Bcnt;++i) Sd[i].clear();
for(int i=1,a,b;i<=Ct;++i) {
a=Blg[fr[i]],b=Blg[to[i]];
if(a!=b) Sd[a].push_back(b);
}
SPFA();
sort(dis+1,dis+Bcnt+1);
printf("%d\n",dis[Bcnt]);
}
return 0;
}
UVa 10510 Cactus
#include<cstdio>
#include<vector>
using namespace std;
const int sm = 2e4+10;
int T,N,M,tot,top;
int Stk[sm],Bn[sm],Lw[sm],Fa[sm];
bool instk[sm],bck[sm],Bcnt;
vector<int> Ed[sm];
int Min(int x,int y) { return x<y?x:y; }
void read(int &x) {
char ch=getchar();x=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void initial() {
tot=Bcnt=0;
for(int i=1;i<=N;++i) {
Bn[i]=Fa[i]=instk[i]=bck[i]=0;
Ed[i].clear();
}
for(int i=1,u,v;i<=M;++i) {
read(u),read(v);
Ed[u+1].push_back(v+1);
}
while(top) Stk[top--]=0;
}
bool Back(int u,int v) {
while(Fa[u]!=v) {
if(bck[u]) return 0;
bck[u]=1,u=Fa[u];
if(Fa[u]==v) {
if(bck[u]) return 0;
bck[u]=1;
}
}
return 1;
}
bool Tarjan(int x) {
int j;
Bn[x]=Lw[x]=++tot;
Stk[++top]=x,instk[x]=1;
for(int i=0;i<Ed[x].size();++i) {
j=Ed[x][i];
if(!Bn[j]) {
Fa[j]=x;
if(!Tarjan(j)) return 0;
Lw[x]=Min(Lw[x],Lw[j]);
} else if(instk[j]) {
if(!Back(x,j)) return 0;
Lw[x]=Min(Lw[x],Lw[j]);
}
}
if(Bn[x]==Lw[x]) {
if(Bcnt) return 0; Bcnt=1;
do {
j=Stk[top--];
instk[j]=0;
}while(x!=j);
}
return 1;
}
int main() {
read(T);
while(T--) {
read(N),read(M),initial();
puts((Tarjan(1)&&tot==N)?"YES":"NO");
}
return 0;
}
POJ 1904 King’s Quest
哈哈伟大的输出优化…
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int sm = 2000+5;
const int sn = 200000+2000+10;
int N,top,tot,Ct,Bcnt;
int to[sn],nxt[sn],hd[sm<<1],Ans[sm];
int Bn[sm<<1],Lw[sm<<1],Stk[sm<<1],instk[sm<<1],Blg[sm<<1];
void read(int &x) {
char ch=getchar();x=0;
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void Out(int a) {
if(a>9) Out(a/10);
putchar(a%10+'0');
}
void Add(int u,int v) {
to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct;
}
int Min(int x,int y) { return x<y?x:y; }
void Tarjan(int x) {
int j;
Bn[x]=Lw[x]=++tot;
Stk[++top]=x,instk[x]=true;
for(int i=hd[x];i;i=nxt[i]) {
j=to[i];
if(!Bn[j]) {
Tarjan(j);
Lw[x]=Min(Lw[x],Lw[j]);
}
else if(instk[j]&&Bn[j]<Lw[x])
Lw[x]=Bn[j];
}
if(Lw[x]==Bn[x]) {
++Bcnt;
do {
j=Stk[top--];
instk[j]=false;
Blg[j]=Bcnt;
}while(j!=x);
}
}
int main() {
int k,x,len;
read(N);
for(int i=1;i<=N;++i) {
read(k);
for(int j=1;j<=k;++j)
read(x),Add(i,x+N);
}
for(int i=1;i<=N;++i)
read(x),Add(x+N,i);
for(int i=1;i<=2*N;++i)
if(!Bn[i]) Tarjan(i);
for(int i=1;i<=N;++i) {
len=0;
for(int j=hd[i];j;j=nxt[j])
if(Blg[i]==Blg[to[j]])
Ans[++len]=to[j]-N;
sort(Ans+1,Ans+len+1);
Out(len);
for(int j=1;j<=len;++j)
putchar(32),Out(Ans[j]);
putchar(10);
}
return 0;
}