第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
对于100%的数据, N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8。
tarjan缩点+topsort。
强连通的必然是半连通的,那么首先缩点,于是图就变成一条一条的链了。
接下来只要找最长链,并统计最长链的条数即可。
具体见代码。
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> #include <stack> #include <queue> #define M 100005 using namespace std; queue<int> q; int tot=0,n,m,v[M],sccno[M],mod,pre[M],h[M],lowlink[M],dfs_clock,scc_cnt; int du[M],d[M],H[M],cnt[M],num[M]; stack<int> S; queue<int> scc[M]; struct edge { int y,ne; }e[M*15],E[M*15]; void read(int &tmp) { tmp=0; char ch=getchar(); int fu=1; for (;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') fu=-1; for (;ch>='0'&&ch<='9';ch=getchar()) tmp=tmp*10+ch-'0'; tmp*=fu; } void Addedge(int x,int y) { e[++tot].y=y; e[tot].ne=h[x]; h[x]=tot; } void dfs(int x) { pre[x]=lowlink[x]=++dfs_clock; S.push(x); for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (!pre[y]) { dfs(y); lowlink[x]=min(lowlink[x],lowlink[y]); } else if (!sccno[y]) lowlink[x]=min(lowlink[x],pre[y]); } if (pre[x]==lowlink[x]) { scc_cnt++; while (1) { int y=S.top(); S.pop(); cnt[scc_cnt]++; scc[scc_cnt].push(y); sccno[y]=scc_cnt; if (y==x) break; } } } void Findscc() { dfs_clock=scc_cnt=0; for (int i=1;i<=n;i++) pre[i]=0,sccno[i]=0; for (int i=1;i<=n;i++) if (!pre[i]) dfs(i); } void Addedge2(int x,int y) { E[++tot].y=y; E[tot].ne=H[x]; H[x]=tot; } void Buildgragh() { tot=0; for (int i=1;i<=scc_cnt;i++) { while (!scc[i].empty()) { int j=scc[i].front(); scc[i].pop(); for (int k=h[j];k;k=e[k].ne) { int y=sccno[e[k].y]; if (y==i||v[y]==1) continue; v[y]=1; q.push(y); } } while (!q.empty()) { int x=q.front(); du[x]++; q.pop(); Addedge2(i,x); v[x]=0; } } } void Topsort() { int ma=0; tot=0; for (int i=1;i<=scc_cnt;i++) if (!du[i]) { q.push(i); d[i]=cnt[i]; num[i]=1; if (d[i]>ma) ma=d[i]; } while (!q.empty()) { int x=q.front(); q.pop(); for (int i=H[x];i;i=E[i].ne) { int y=E[i].y; du[y]--; if (d[y]==d[x]+cnt[y]) num[y]=(num[y]+num[x])%mod; if (d[y]<d[x]+cnt[y]) { d[y]=d[x]+cnt[y]; num[y]=num[x]; if (ma<d[y]) ma=d[y]; } if (!du[y]) q.push(y); } } for (int i=1;i<=scc_cnt;i++) if (d[i]==ma) tot=(tot+num[i])%mod; printf("%d\n%d\n",ma,(tot+mod)%mod); } int main() { read(n),read(m),read(mod); for (int i=1;i<=m;i++) { int x,y; read(x),read(y); Addedge(x,y); } Findscc(); Buildgragh(); Topsort(); return 0; }
感悟:
1.一开始wa了无数次:
要记录每个强连通分量的点数,更新d[]数组用点数更新,而不是直接+1;
对于方案数num[],也是不能直接+1的,如果当前的d是最大值,才更新,更新的时候要加前一个点的num;如果他更新了最大值,当前点的num等于前一个点的num
2.强连通必然是弱连通的;
一条链上的点弱连通。