解题思路
首先我们知道一个强连通分量肯定是一个半连通子图,于是用 T a r j a n Tarjan Tarjan常规操作先缩点再说。因为这个图是无环的,所以我们可以按照拓扑序进行DP:
f [ i ] f[i] f[i]表示到第i个连通块的最长链大小, a n s [ i ] ans[i] ans[i]表示个数。
很容易就想到了转移的方法:
-
a n s [ i ] = a n s [ i ] + a n s [ j ] {ans[i]=ans[i]+ans[j]} ans[i]=ans[i]+ans[j] ( 条 件 : f [ j ] + s u m [ i ] = = f [ i ] ) (条件:f[j]+sum[i]==f[i]) (条件:f[j]+sum[i]==f[i])
-
a n s [ i ] = a n s [ j ] , f [ i ] = f [ j ] + s u m [ i ] {ans[i]=ans[j],f[i]=f[j]+sum[i]} ans[i]=ans[j],f[i]=f[j]+sum[i] ( 条 件 : f [ j ] + s u m [ i ] > f [ i ] ) (条件:f[j]+sum[i]>f[i]) (条件:f[j]+sum[i]>f[i])
按照这个转移状态就可以啦。
但是这道题就这样结束了吗?不存在的
如果这个缩完点之后的图中出现了重边怎么办,那就会使方案数莫名增多.所以对于缩完点后的图还要判重边:直接让边以起点、终点大小顺序排序,然后只要这条边的起点或者终点与上一条不同就不是重边
所以这道题就弄完了
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n,m,k,tot,top,t,s,maxn,u,v;
int st[200010],sum[200010],head[2000010];
int dfn[200010],low[200010],c[200010],ru[200010],f[200010];
long long X,cnt,ans[200010];
struct c{
int x,next;
}a[2000100];
struct cc{
int x,y;
}b[2000100];
void add(int x,int y){
a[++k].x=y;
a[k].next=head[x];
head[x]=k;
}
bool cmp(cc l,cc r)
{
if(l.x==r.x)return l.y<r.y;
return l.x<r.x;
}
void Tarjan(int x){
st[++top]=x;
dfn[x]=low[x]=++t;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].x;
if(!dfn[y])
{
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(!c[y])
low[x]=min(low[x],low[y]);
}
if(dfn[x]==low[x])
{
tot++;
while(st[top+1]!=x)
{
c[st[top]]=tot;
sum[tot]++;
top--;
}
}
}
int main(){
scanf("%d%d%lld",&n,&m,&X);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
b[i].x=u,
b[i].y=v;
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan(i);
memset(a,0,sizeof(a));
memset(head,0,sizeof(head));
k=0;
for(int i=1;i<=m;i++)
{
b[i].x=c[b[i].x];
b[i].y=c[b[i].y];
}
sort(b+1,b+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(b[i].x!=b[i].y&&(b[i-1].x!=b[i].x||b[i-1].y!=b[i].y))
add(b[i].x,b[i].y),ru[b[i].y]++;
}
queue<int>q;
for(int i=1;i<=tot;i++)
{
ans[i]=1;
f[i]=sum[i];
if(!ru[i])q.push(i);
}
while(!q.empty())
{
int w=q.front();
q.pop();
maxn=max(maxn,f[w]);
for(int j=head[w];j;j=a[j].next)
{
int y=a[j].x;
ru[y]--;
if(f[y]<f[w]+sum[y])
{
f[y]=f[w]+sum[y];
ans[y]=ans[w];
}
else if(f[y]==(f[w]+sum[y]))
ans[y]=(ans[y]+ans[w])%X;
if(!ru[y])q.push(y);
}
}
for(int i=1;i<=tot;i++)
if(f[i]==maxn)cnt=(cnt+ans[i])%X;
printf("%d\n%lld",maxn,cnt);
}