您的位置:首页 > 其它

BZOJ 1093 浅谈tarjan缩点DAG最长路径即拓扑排序

2017-07-10 15:49 393 查看


(这次的图是自己画的2333)

世界真的很大

这道题有点扯,思路不太好想

关键是想清楚半连通分量的等价类

然后就好办了

有一些坑点

先看一下题吧

description:

 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意
两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,
则称G'是G的一个导出子图。若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。若G'是G所有半连通子图
中包含节点数最多的,则称G'是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K
,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。


input

第一行包含3个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整
数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤1
00000, M ≤1000000;对于100%的数据, X ≤10^8


output

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.


首先应该明确半连通分量是个什么东西

有向图,分量,自然先想到强连通分量

如果点u到达点v,点v不到达点u,点v所在的强连通分量u也全部可以到达,而全部不能到达v

如果能的话u和v就在一个强连通分量里了2333

即半连通分量就是强连通分量缩点后的一条链

即求缩点后的图里的最长链

可以看一下这篇

大体来说采用拓扑排序的思路

用bfs的方法更新状态

有个坑点就是两个强连通分量缩点后的点之间有可能不止一条边,可能会导致重复更新

对每一个点记录一下现在的点是由哪一个点更新来的,避免重复

没什么了

完整代码:

#include<stdio.h>
#include<stack>
#include<queue>
#include<cstring>
using namespace std;
struct edge
{
int last,v,u;
}ed[2000010],ad[2000010];

stack <int> stk;
queue <int> state;

int head[200010],place[200010],vis[200010],ins[200010];
int dfn[200010],low[200010],siz[200010],in[200010];
int dis[200010],nmb[200010];
int num=0,cnt=0,idx=0,mum=0,ans=0,bns=0,n,m,mod,tar;

void add(int u,int v)
{
num++;
ed[num].v=v;
ed[num].u=u;
ed[num].last=head[u];
head[u]=num;
}

void ade(int u,int v)
{
mum++;
ad[mum].v=v;
ad[mum].last=head[u];
head[u]=mum;
}

void tarjan(int u)
{
dfn[u]=low[u]=++idx;
vis[u]=ins[u]=1;
stk.push(u);
for(int i=head[u];i;i=ed[i].last)
{
int v=ed[i].v;
if(!vis[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u])
{
cnt++;int t=-1;
while(t!=u)
{
t=stk.top();
place[t]=cnt;
siz[cnt]++;
ins[t]=0;
stk.pop();
}
}
}

void rebuild()
{
memset(head,0,sizeof(head));
for(int i=1;i<=num;i++)
if(place[ed[i].u]!=place[ed[i].v])
{
ade(place[ed[i].u],place[ed[i].v]);
in[place[ed[i].v]]++;
}
}

void top()
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=cnt;i++)
if(in[i]==0)
{
state.push(i);
dis[i]=siz[i];nmb[i]=1;
}
while(!state.empty())
{
int u=state.front();
state.pop();
for(int i=head[u];i;i=ad[i].last)
{
int v=ad[i].v;
in[v]--;
if(in[v]==0) state.push(v);
if(vis[v]==u) continue ;
if(dis[v]<dis[u]+siz[v])
{
dis[v]=dis[u]+siz[v];
nmb[v]=nmb[u];
}
else if(dis[v]==dis[u]+siz[v])
nmb[v]=(nmb[u]+nmb[v])%mod;
vis[v]=u;
}
}
for(int i=1;i<=cnt;i++)
if(dis[i]>ans) ans=dis[i];
for(int i=1;i<=cnt;i++)
if(dis[i]==ans)
bns=(bns+nmb[i])%mod;
}

int main()
{
scanf("%d%d%d",&n,&m,&mod);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++)
if(!vis[i]) tarjan(i);
rebuild();
top();
printf("%d\n%d",ans,bns%mod);
return 0;
}


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: