title
BZOJ 1093
LUOGU 2272
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
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤100000, M ≤1000000;对于100%的数据, X ≤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
analysis
首先观察到这个图中如果有某个部分是强连通的,那其实可以把这整个部分都看做一个点,这样并不会对结果产生影响。
所以首先对这个图进行 缩点,并记录下每个连通块的大小,和每个点所在的连通块。
缩点之后,会发现这个图变成了一个 (有向无环图),我们要对这个 求出最长链的大小和个数。
因为这个图是无环的,所以我们可以按照拓扑序进行 : 表示到第 个连通块的最长链大小, 表示个数。
很容易就想到了转移的方法:
-
1.
-
2.
按照这个转移状态就可以啦。
刚开始对这个状态转移不太理解,一直觉得应该是
转移到
才对,然而试了半天都是不对的,
那位神犇路过,请解答一下,感激不尽啊。。
但是要注意缩点之后会出现大量重边拓扑排序之前要去重,所以用 存储缩点后的图还是挺好的,排序去重比较方便。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxm<<1],Next[maxm<<1],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int dfn[maxn],low[maxn],id;
int Stack[maxn],top;
int belong[maxn],siz[maxn],tot;
bool instack[maxn];
inline void tarjan(int x)
{
dfn[x]=low[x]=++id;
Stack[++top]=x;
instack[x]=1;
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if (instack[y])
low[x]=min(low[x],dfn[y]);
}
if (low[x]==dfn[x])
{
int k;
++tot;
do
{
k=Stack[top--];
belong[k]=tot;
instack[k]=0;
++siz[tot];
} while (k!=x);
}
}
vector<int>E[maxm];
int length[maxn],deg[maxn];
int ans[maxn],cnt;
inline void Topsort()
{
queue<int>q;
for (int i=1; i<=tot; ++i)
if (!deg[i]) q.push(i),E[0].push_back(i),++length[0];
while (!q.empty())
{
int x=q.front();
q.pop();
ans[++cnt]=x;
for (int i=0; i<length[x]; ++i)
if (!--deg[E[x][i]]) q.push(E[x][i]);
}
}
int f[maxn],g[maxn];
int main()
{
int n,m,mod;
read(n);read(m);read(mod);
for (int i=1; i<=m; ++i)
{
int a,b;
read(a);read(b);
add(a,b);
}
//缩点
for (int i=1; i<=n; ++i)
if (!dfn[i]) tarjan(i);
for (int x=1; x<=n; ++x)
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (belong[x]==belong[y]) continue;
E[belong[x]].push_back(belong[y]);
}
for (int i=1; i<=tot; ++i) sort(E[i].begin(),E[i].end());
for (int i=1; i<=tot; ++i) length[i]=unique(E[i].begin(),E[i].end())-E[i].begin();
for (int i=1; i<=tot; ++i)
for (int j=0; j<length[i]; ++j)
++deg[E[i][j]];
Topsort();
for (int i=tot; ~i; --i)
{
int x=ans[i];
f[x]=siz[x],g[x]=1;
for (int j=0; j<length[x]; ++j)
{
int y=E[x][j];
if (f[x]<f[y]+siz[x])
f[x]=f[y]+siz[x],g[x]=g[y];
else if (f[x]==f[y]+siz[x])
(g[x]+=g[y])%=mod;
}
}
printf("%d\n%d\n",f[0],g[0]);
return 0;
}