您的位置:首页 > Web前端

[CodeChef FEB14]Graph Challenge解题报告(求半支配点)

2015-12-02 20:59 363 查看

题意

给一张有向图,使得从1开始按某种顺序DFS,可以让每个点的标号等于其DFS序号。求每个点的半支配点。
http://cojs.tk/cogs/problem/problem.php?pid=2117

题解

使用Lengauer Tarjan算法,对这一算法的描述和证明见我的上一篇博文:
/article/7969134.html

当然本题只需要求半支配点。
首先按照适当顺序DFS,还原题目描述中所称的DFS生成树。然后直接套算法。
代码如下,思路很简单。要点都写在注释中了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int SIZEN=100010;
void getint(int &x){
char c=0;
for(;c<'0'||c>'9';c=getchar());
for(x=0;'0'<=c&&c<='9';c=getchar()) x=x*10+c-'0';
}
int N,M,Q;
vector<int> c[SIZEN],ct[SIZEN];
int timer=0,fa[SIZEN];
int ufs[SIZEN],best[SIZEN],sdom[SIZEN];
int find(int x){//将x连接到路径最上端,并计算出一路上诸点的best
if(x==ufs[x]) return x;
int y=find(ufs[x]);
if(sdom[best[ufs[x]]]<sdom[best[x]]){//注意ufs[x]≠y
best[x]=best[ufs[x]];
}
return ufs[x]=y;
}
void Lengauer_Tarjan(void){
for(int i=1;i<=N;i++) ufs[i]=best[i]=sdom[i]=i;
for(int i=N;i>=1;i--){
for(int j=0;j<ct[i].size();j++){
/*对于<i的那些,find之后是它本身
对于>i的那些,find之后得到了它到根的最优sdom
注意,对于第二种情况,u~v这一路上必然全大于i*/
int u=ct[i][j];
find(u);
sdom[i]=min(sdom[i],sdom[best[u]]);
}
ufs[i]=fa[i];
}
}
bool vis[SIZEN];
void DFS(int x){
vis[x]=true;
timer++;
for(int i=0;i<c[x].size();i++){
int u=c[x][i];
if(!vis[u]&&u==timer+1){
fa[u]=x;
DFS(u);
}
}
}
int ans[SIZEN];
void work(void){
timer=0;
memset(vis,0,sizeof(vis));
fa[1]=0;
DFS(1);
Lengauer_Tarjan();
memset(ans,0,sizeof(ans));
for(int i=2;i<=N;i++) ans[sdom[i]]++;
int x;
for(int i=1;i<=Q;i++){
getint(x);
printf("%d ",ans[x]);
}
printf("\n");
}
void read(void){
getint(N);getint(M);getint(Q);
for(int i=1;i<=N;i++) c[i].clear(),ct[i].clear();
int a,b;
for(int i=1;i<=M;i++){
getint(a);
getint(b);
c[a].push_back(b);
ct[b].push_back(a);
}
for(int i=1;i<=N;i++) sort(c[i].begin(),c[i].end());
}
int main(void){
//freopen("input.in","r",stdin);
int T;getint(T);
while(T--){
read();
work();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: