Description
一个有向图 (G=(V,E)) 称为半连通的((Semi-Connected)),如果满足:$ forall u,v in V$,满足 (u→v) 或 (v→u),即对于图中任意两点 (u),(v) ,存在一条 (u) 到 (v) 的有向路径或者从 (v) 到 (u) 的有向路径。若 (G'=(V',E')) 满足 (E') 是 (E) 中所有跟 (V') 有关的边,则称 (G') 是 (G) 的一个导出子图。若 (G') 是 (G) 的导出子图,且 (G') 半连通,则称 (G') 为 (G) 的半连通子图。若 (G') 是 (G) 所有半连通子图中包含节点数最多的,则称 (G') 是 (G) 的最大半连通子图。给定一个有向图 (G) ,请求出 (G) 的最大半连通子图拥有的节点数 (K),以及不同的最大半连通子图的数目 (C)。由于 (C) 可能比较大,仅要求输出 (C) 对 (X) 的余数。
Input
第一行包含两个整数 (N) ,(M),(X) 。(N),(M) 分别表示图 (G) 的点数与边数,(X) 的意义如上文所述接下来 (M) 行,每行两个正整数 (a),$ b$,表示一条有向边 ((a, b)) 。图中的每个点将编号为 (1,2,3…N),保证输入中同一个 ((a,b)) 不会出现两次。(N leq 100000),$ M leq 1000000$;对于 (100 \%) 的数据, (X leq 10^8)
Output
应包含两行,第一行包含一个整数 (K) 。第二行包含整数 $ C$ (Mod) (X).
Sample Input
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
3
题解
一道不错的题。
“半连通子图”看上去有些棘手,那先 (tarjan) 缩环,把原先的有向图变成 (DAG)
然后稍微推一下会发现,半连通子图相当于 (DAG) 中的一条链,我们要求的就是 (DAG) 中的最长链及其个数
(dp) 一下就好了。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100005;
struct node{
int v;
node *next;
}pool[N*10],*h[N],pool2[N*10],*h2[N];
int cnt,cnt2;
void addedge(int u,int v){
node *p=&pool[++cnt];
p->v=v;p->next=h[u];h[u]=p;
}
int n,m,X;
int dfn[N],low[N],tot,vis[N];
int st[N],top,scc[N],sccno,size[N];
void dfs(int u){
int v;
dfn[u]=low[u]=++tot;
st[top++]=u;
vis[u]=1;
for(node *p=h[u];p;p=p->next)
if(!vis[v=p->v]){
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]==1) low[u]=min(low[u],dfn[v]);
if(low[u]==dfn[u]){
sccno++;
for(;;){
size[sccno]++;
vis[v=st[--top]]=2;
scc[v]=sccno;
if(u==v) break;
}
}
}
struct edge{
int u,v;
bool operator < (const edge &b) const{
if(scc[u]!=scc[b.u]) return scc[u]<scc[b.u];
return scc[v]<scc[b.v];
}
}ed[N*10];
void addedge2(int u,int v){
node *p=&pool2[++cnt2];
p->v=v;p->next=h2[u];h2[u]=p;
}
int ml[N],mt[N];
void dp(int u){
if(ml[u]!=-1) return;
int v;
ml[u]=size[u]; mt[u]=1;
for(node *p=h2[u];p;p=p->next){
dp(v=p->v);
if(ml[u]<ml[v]+size[u]) ml[u]=ml[v]+size[u],mt[u]=mt[v];
else if(ml[u]==ml[v]+size[u]) mt[u]=(mt[u]+mt[v])%X;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&X);
for(int i=0;i<m;i++)
scanf("%d%d",&ed[i].u,&ed[i].v),addedge(ed[i].u,ed[i].v);
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i);
sort(ed,ed+m);
for(int i=0;i<m;i++){
if(i!=0 && scc[ed[i].u]==scc[ed[i-1].u] && scc[ed[i].v]==scc[ed[i-1].v])
continue;
if(scc[ed[i].u]==scc[ed[i].v]) continue;
addedge2(scc[ed[i].u],scc[ed[i].v]);
}
memset(ml,-1,sizeof(ml));
for(int i=1;i<=n;i++)
if(ml[i]==-1) dp(i);
int ans=0,t=0;
for(int i=1;i<=n;i++)
if(ml[i]>ans) ans=ml[i],t=mt[i];
else if(ml[i]==ans) t=(t+mt[i])%X;
printf("%d
%d
",ans,t);
return 0;
}