UOJGraph(tarjan缩点+拓扑)
2016-11-07 13:56
260 查看
Description
给出 NN 个点, MM 条边的有向图, 对于每个点 vv, 求 A(v)A(v) 表示从点 vv 出发, 能到达的编号最大的点。
Input
第 11 行, 22 个整数 NN, MM。 接下来 MM 行, 每行 22 个整数 UiUi, ViVi, 表示边 ⟨Ui,Vi⟩⟨Ui,Vi⟩。点用 1,2,...,N1,2,...,N 编号。
Output
NN 个整数 A(1),A(2),...,A(N)A(1),A(2),...,A(N)。
Sample
[input]
4 3
1 2
2 4
4 3
[output]
4 4 3 4
Note
对于 60% 的数据, 1 ≤ N, K ≤ 103103;
对于 100% 的数据, 1 ≤ N, M ≤ 105105。
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#define maxn 100005
using namespace std;
typedef pair<int,int>pa;
set<pa>mp;
stack<int>s;
int n,m,to[maxn],mx[maxn],from[maxn],pre[maxn],low[maxn],tme=0,fst[maxn],nxt[maxn],scc[maxn];
int scccnt=0,cnt=0,d[maxn],ft[maxn],nt[maxn],ct=0,de[maxn],t[maxn];
void add(int x,int y){to[++cnt]=y;from[cnt]=x;nxt[cnt]=fst[x];fst[x]=cnt;}
void add2(int x,int y){t[++ct]=y;nt[ct]=ft[x];ft[x]=ct;++de[y];}
int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
void dfs(int x){
s.push(x);
pre[x]=low[x]=++tme;
for(int i=fst[x];i;i=nxt[i]){
if(!pre[to[i]]){
dfs(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!scc[to[i]]){
low[x]=min(low[x],pre[to[i]]);
}
}
if(low[x]==pre[x]){
++scccnt;
while(1){
int y=s.top();s.pop();
scc[y]=scccnt;
mx[scccnt]=max(mx[scccnt],y);
if(y==x)break;
}
}
}
void init(){
n=get(),m=get();int x,y;
for(int i=1;i<=m;++i){
x=get();y=get();
add(x,y);++d[y];
}
for(int i=1;i<=n;++i)if(!pre[i])dfs(i); //注意
}
queue<int>q;
void bfs(){
while(q.size()){
int x=q.front();q.pop();
for(int i=ft[x];i;i=nt[i]){
--de[t[i]];mx[t[i]]=max(mx[t[i]],mx[x]);
if(de[t[i]]==0)q.push(t[i]),de[t[i]]=-1;
}
}
}
void solveit(){
for(int i=1;i<=cnt;++i){
int x=scc[to[i]],y=scc[from[i]];
if(x!=y && !mp.count(make_pair(x,y))){ //注意
add2(x,y);mp.insert(make_pair(x,y));
}
}
for(int i=1;i<=scccnt;++i)if(de[i]==0)q.push(i),de[i]=-1;
bfs();
for(int i=1;i<n;++i)printf("%d ",mx[scc[i]]);
printf("%d",mx[scc
]);
}
int main(){
init();
solveit();
return 0;
}
【注意】
*连通块连边时要判断是否为同一连通块
*从1~n未pre[ ]的进入dfs而不是d[ ]为0的
给出 NN 个点, MM 条边的有向图, 对于每个点 vv, 求 A(v)A(v) 表示从点 vv 出发, 能到达的编号最大的点。
Input
第 11 行, 22 个整数 NN, MM。 接下来 MM 行, 每行 22 个整数 UiUi, ViVi, 表示边 ⟨Ui,Vi⟩⟨Ui,Vi⟩。点用 1,2,...,N1,2,...,N 编号。
Output
NN 个整数 A(1),A(2),...,A(N)A(1),A(2),...,A(N)。
Sample
[input]
4 3
1 2
2 4
4 3
[output]
4 4 3 4
Note
对于 60% 的数据, 1 ≤ N, K ≤ 103103;
对于 100% 的数据, 1 ≤ N, M ≤ 105105。
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#define maxn 100005
using namespace std;
typedef pair<int,int>pa;
set<pa>mp;
stack<int>s;
int n,m,to[maxn],mx[maxn],from[maxn],pre[maxn],low[maxn],tme=0,fst[maxn],nxt[maxn],scc[maxn];
int scccnt=0,cnt=0,d[maxn],ft[maxn],nt[maxn],ct=0,de[maxn],t[maxn];
void add(int x,int y){to[++cnt]=y;from[cnt]=x;nxt[cnt]=fst[x];fst[x]=cnt;}
void add2(int x,int y){t[++ct]=y;nt[ct]=ft[x];ft[x]=ct;++de[y];}
int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
void dfs(int x){
s.push(x);
pre[x]=low[x]=++tme;
for(int i=fst[x];i;i=nxt[i]){
if(!pre[to[i]]){
dfs(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!scc[to[i]]){
low[x]=min(low[x],pre[to[i]]);
}
}
if(low[x]==pre[x]){
++scccnt;
while(1){
int y=s.top();s.pop();
scc[y]=scccnt;
mx[scccnt]=max(mx[scccnt],y);
if(y==x)break;
}
}
}
void init(){
n=get(),m=get();int x,y;
for(int i=1;i<=m;++i){
x=get();y=get();
add(x,y);++d[y];
}
for(int i=1;i<=n;++i)if(!pre[i])dfs(i); //注意
}
queue<int>q;
void bfs(){
while(q.size()){
int x=q.front();q.pop();
for(int i=ft[x];i;i=nt[i]){
--de[t[i]];mx[t[i]]=max(mx[t[i]],mx[x]);
if(de[t[i]]==0)q.push(t[i]),de[t[i]]=-1;
}
}
}
void solveit(){
for(int i=1;i<=cnt;++i){
int x=scc[to[i]],y=scc[from[i]];
if(x!=y && !mp.count(make_pair(x,y))){ //注意
add2(x,y);mp.insert(make_pair(x,y));
}
}
for(int i=1;i<=scccnt;++i)if(de[i]==0)q.push(i),de[i]=-1;
bfs();
for(int i=1;i<n;++i)printf("%d ",mx[scc[i]]);
printf("%d",mx[scc
]);
}
int main(){
init();
solveit();
return 0;
}
【注意】
*连通块连边时要判断是否为同一连通块
*从1~n未pre[ ]的进入dfs而不是d[ ]为0的
相关文章推荐
- BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】
- BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】
- BZOJ 1924 [Sdoi2010]所驼门王的宝藏 tarjan缩点+拓扑DP
- 洛谷p3387 拓扑+tarjan缩点
- BZOJ 1512 [POI2006]Pro-Professor Szu Tarjan缩点+拓扑DP
- 【判定弱连通】==【tarjan求scc + 缩点+拓扑】
- HDU 6165 FFF at Valentine (tarjan缩点+拓扑判任意两点联通)
- [BZOJ 1093 && YZOI1172] Tarjan缩点+拓扑DP 最大半连通子图
- 【Tarjan缩点】PO3352 Road Construction
- Tarjan缩点
- POJ 3352 Road Construction POJ 3177Redundant Paths(Tarjan缩点)
- POJ2375 暴力建图+tarjan缩点
- HDU3836 Tarjan缩点
- poj 3592 Instantaneous Transference(tarjan + 缩点 + 最长路)
- poj 1236+hdu2767 有向图 缩点+看度数(tarjan)
- [Tarjan缩点]BZOJ1051 YZOI2099题解
- BZOJ 2438 杀人游戏 强联通分量tarjan缩点
- srm 299 div2 1000(强连通分量缩点,拓扑)
- POJ 2762 tarjan缩点+并查集+度数
- poj 3648 Wedding 【2-sat 经典建图 输出一组可行解 好题】 【tarjan求SCC + 缩点 + 拓扑排序 + 染色】