题目大意:n个点,m条有向边,从每个入度为0的点走到编号为n的点,求经过次数最多的边的经过次数
题解:把边拆成两点,由乘法原理
一条边(a,b)的经过次数=起点到a的路径条数*b到编号为n的路径条数
建正反图后dp就行
我的收获:点/边的转化,数学(
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int M=50005;
int n,m,t,ans;
int head[M],ui[M],vi[M];
int f[M],g[M];
struct edge{int to,nex;}e[M*2];
void add(int u,int v){e[t]=(edge){v,head[u]};head[u]=t++;}
void dp1(int x)
{
if(head[x]==-1){f[x]=1;return;}
for(int i=head[x];i!=-1;i=e[i].nex)
{
if(!f[e[i].to]) dp1(e[i].to);
f[x]+=f[e[i].to];
}
}
void dp2(int x)
{
if(head[x]==-1){g[x]=1;return;}
for(int i=head[x];i!=-1;i=e[i].nex)
{
if(!g[e[i].to]) dp2(e[i].to);
g[x]+=g[e[i].to];
}
}
void work()
{
for(int i=1;i<=n;i++) if(!f[i]) dp1(i);
t=0;memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++) add(vi[i],ui[i]);
dp2(n);
for(int i=1;i<=m;i++) ans=max(ans,g[ui[i]]*f[vi[i]]);
printf("%d\n",ans);
}
void init()
{
t=0;memset(head,-1,sizeof(head));
cin>>n>>m;
for(int i=1;i<=m;i++)
scanf("%d%d",&ui[i],&vi[i]),add(ui[i],vi[i]);
}
int main()
{
init();
work();
return 0;
}