这个题,翻译一下题面,就是一个连通图,找他的最长链的数量。。。
所以说方法就比较明显了:tarjan缩点+拓扑+DP
注意也是本题唯一坑点,拓扑DP的时候要考虑重复边的情况。。。
呆码:
#include<iostream> #include<cstdio> #define N 100010 #define M 1000010 using namespace std; struct asd{ int nxt; int to; } a[M<<2],b[M]; int head[M<<2],headd[M],t[N],dfn[N],low[N],stack[N]; int num[N],belong[N],tmp[N],f[N],g[N],vis[N]; int sum,number,top,cnt,n,m,mo,mx,ans; bool use[N]; inline void add(int x,int y) { a[++sum].nxt=head[x]; a[sum].to=y; head[x]=sum; } inline void bdd(int x,int y) { b[++sum].nxt=headd[x]; b[sum].to=y; headd[x]=sum; t[y]++; } inline void tarjan(int u) { number++; dfn[u]=low[u]=number; stack[++top]=u; use[u]=1; for(int i=head[u];i;i=a[i].nxt) { int v=a[i].to; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(use[v]) low[u]=min(low[u],dfn[v]); } int v; if(dfn[u]==low[u]) { cnt++; do{ num[cnt]++; v=stack[top--]; belong[v]=cnt; use[v]=0; }while(u!=v); } } inline void rebuild() { for(int i=1;i<=n;i++) for(int j=head[i];j;j=a[j].nxt) if(belong[i]!=belong[a[j].to]) bdd(belong[i],belong[a[j].to]); } int main() { scanf("%d%d%d",&n,&m,&mo); int x,y; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); sum=0; rebuild(); int Head=0,Tail=0; for(int i=1;i<=cnt;i++) { if(!t[i]) tmp[++Tail]=i; f[i]=num[i]; g[i]=1; } while(Head<Tail) { int now=tmp[++Head]; for(int i=headd[now];i;i=b[i].nxt) { int v=b[i].to; t[v]--; if(!t[v]) tmp[++Tail]=v; if(vis[v]==now) continue; if(f[now]+num[v]>f[v]) { f[v]=f[now]+num[v]; g[v]=g[now]; } else if(f[now]+num[v]==f[v]) g[v]=(g[v]+g[now])%mo; vis[v]=now; } } for(int i=1;i<=cnt;i++) { if(f[i]>mx) mx=f[i],ans=g[i]; else if(f[i]==mx) ans=(ans+g[i])%mo; } printf("%d %d",mx,ans); }