tarjan
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <iostream>
using namespace std; #define ll long long const int maxn=1e4+;
const int inf=1e9;
const double eps=1e-; struct node
{
int d;
node *to;
}*e[maxn]; int id,dfn[maxn],low[maxn],tot_st,st[maxn],m,group[maxn];
bool vis[maxn],vis_st[maxn]; /**
id:编号 每增加一个点,++id
每增加一个点,加入栈中
low:点的编号
dfn:点以及后续点通过一条边能到的最小编号点的编号 同一个类 dfn[d]=low[d] 栈中,第x个数d到栈顶 任意点的low值为dfn[d]
st:栈 若点变为在一个类中时,从栈中弹出
vis_st : 当点还未标记到类中时,可以使用 m:类的个数
group:每个点所在的类
**/ void tarjan(int d)
{
int dd;
vis[d]=;
dfn[d]=low[d]=++id;
st[++tot_st]=d;
node *p=e[d];
while (p)
{
dd=p->d;
if (!vis[dd])
{
tarjan(dd);
low[d]=min(low[d],low[dd]);
}
else if (!vis_st[dd])
low[d]=min(low[d],dfn[dd]);
p=p->to;
}
if (dfn[d]==low[d])
{
m++;
int g=tot_st;
while (st[tot_st]!=d)
{
group[st[tot_st]]=m;
vis_st[st[tot_st]]=;
tot_st--;
}
group[st[tot_st]]=m;
vis_st[st[tot_st]]=;
tot_st--;
g-=tot_st; ///类的大小
}
} int main()
{
node *p;
int n,q,x,y,i;
scanf("%d%d",&n,&q);
while (q--)
{
scanf("%d%d",&x,&y);
///注意单边还是双边
p=new node();
p->d=y;
p->to=e[x];
e[x]=p;
}
for (i=;i<=n;i++)
if (!vis[i])
tarjan(i);
return ;
}
/* */
luogu P1726 上白泽慧音
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <iostream>
using namespace std; #define ll long long const int maxn=1e4+;
const int inf=1e9;
const double eps=1e-; struct node
{
int d;
node *to;
}*e[maxn]; int id,dfn[maxn],low[maxn],tot_st,st[maxn],m,group[maxn]; ///m:类的个数
bool vis[maxn],vis_st[maxn]; ///st:栈,存储未被'类‘标签的点 int ind,maxg=,md; void tarjan(int d)
{
int dd;
vis[d]=;
dfn[d]=low[d]=++id;
st[++tot_st]=d;
node *p=e[d];
while (p)
{
dd=p->d;
if (!vis[dd])
{
tarjan(dd);
low[d]=min(low[d],low[dd]);
}
else if (!vis_st[dd])
low[d]=min(low[d],dfn[dd]);
p=p->to;
}
if (dfn[d]==low[d])
{
m++;
int g=tot_st,mind=inf;
while (st[tot_st]!=d)
{
group[st[tot_st]]=m;
mind=min(mind,st[tot_st]);
vis_st[st[tot_st]]=;
tot_st--;
}
group[st[tot_st]]=m;
mind=min(mind,st[tot_st]);
vis_st[st[tot_st]]=;
tot_st--;
g-=tot_st;
if (maxg<g || (maxg==g && mind<md))
ind=m,maxg=g,md=mind;
}
} int main()
{
node *p;
int n,q,x,y,z,i;
scanf("%d%d",&n,&q);
while (q--)
{
scanf("%d%d%d",&x,&y,&z);
p=new node();
p->d=y;
p->to=e[x];
e[x]=p;
if (z==)
{
p=new node();
p->d=x;
p->to=e[y];
e[y]=p;
}
}
for (i=;i<=n;i++)
if (!vis[i])
tarjan(i);
printf("%d",maxg);
bool v=;
for (i=;i<=n;i++)
if (group[i]==ind)
{
if (!v)
printf("\n"),v=;
else
printf(" ");
printf("%d",i);
}
return ;
}
/* */
luogu P3387 【模板】缩点
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <iostream>
using namespace std; #define ll long long const int maxn=1e4+;
const int inf=1e9;
const double eps=1e-; /*
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。 第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边 共一行,最大的点权之和。 2 2
1 1
1 2
2 1 2
*/ struct node
{
int d;
node *to;
}*e[maxn],*gr[maxn]; int id,dfn[maxn],low[maxn],tot_st,st[maxn],m,group[maxn];
bool vis[maxn],vis_st[maxn];
int v[maxn],ans[maxn];/// /**
id:编号 每增加一个点,++id
每增加一个点,加入栈中
low:点的编号
dfn:点以及后续点通过一条边能到的最小编号点的编号 同一个类 dfn[d]=low[d] 栈中,第x个数d到栈顶 任意点的low值为dfn[d]
st:栈 若点变为在一个类中时,从栈中弹出
vis_st : 当点还未标记到类中时,可以使用 m:类的个数
group:每个点所在的类
**/ void tarjan(int d)
{
int dd;
vis[d]=;
dfn[d]=low[d]=++id;
st[++tot_st]=d;
node *p=e[d];
while (p)
{
dd=p->d;
if (!vis[dd])
{
tarjan(dd);
low[d]=min(low[d],low[dd]);
}
else if (!vis_st[dd])
low[d]=min(low[d],dfn[dd]);
p=p->to;
}
if (dfn[d]==low[d])
{
m++;
while (st[tot_st]!=d)
{
group[st[tot_st]]=m; ///
vis_st[st[tot_st]]=;
tot_st--;
}
group[st[tot_st]]=m; ///
vis_st[st[tot_st]]=;
tot_st--;
}
} void dfs(int d)
{
node *p=gr[d];
int dd,add=;
vis[d]=;
while (p)
{
dd=p->d;
if (!vis[dd])
dfs(dd);
add=max(add,ans[dd]); ///点d到达下一个点,最大的值 千万注意这个是放在外面的
p=p->to;
}
ans[d]+=add;
} int main()
{
node *p,*r;
int n,q,x,y,i,d;
scanf("%d%d",&n,&q); for (i=;i<=n;i++) ///点权
scanf("%d",&v[i]); while (q--)
{
scanf("%d%d",&x,&y);
///注意单边还是双边
p=new node();
p->d=y;
p->to=e[x];
e[x]=p;
}
for (i=;i<=n;i++)
if (!vis[i])
tarjan(i); for (i=;i<=n;i++)
{
ans[group[i]]+=v[i]; ///同一类,能互相访问所有的点
p=e[i];
while (p)
{
d=p->d;
if (group[i]!=group[d])
{
r=new node(); ///变量名要有区分
r->d=group[d];
r->to=gr[group[i]];
gr[group[i]]=r; // printf("%d %d\n",group[i],group[d]);
}
p=p->to;
}
} ///tarjan+缩点后无环
memset(vis,,sizeof(vis));
for (i=;i<=n;i++)
if (!vis[i])
dfs(i);
int maxa=;
for (i=;i<=n;i++)
maxa=max(maxa,ans[i]);
printf("%d",maxa);
return ;
}
/*
3 2
1 2 3
1 2
1 3
*/
luogu P3388 【模板】割点(割顶)