第一次写缩点是用vector存边,因此现在用链前存边再写一遍
传送门:P3387 【模板】缩点
题意分析(1)很容易发现,由于每一个点可以被经过多次,那么同一个强连通分量中的每一个点都可以互相到达,因此可以看作一个点。
(2)缩点后,整张图一定变成了一个DAG
证明:反证即可,假设不是DAG,则有环存在,环是强连通分量,而缩点后把每一个强连通分量都缩成了一个点,新图中显然没有强连通分量,假设不成立,则新图一定是DAG
(3)然后就是求一条最长路径。
方法一:用spfa跑最长路
方法二:拓扑排序dp:
无后效性的证明
dp[i]是以i为终点的路径的最大点权和
因为拓扑序中可以保证对于图中∀边(u,v),u一定在v的前面,这样的话当我需要更新dp[v]的值时,需要用到的dp[u]的值都已经处理过了,dp[v]的值就不会再被它后面遍历到的点的dp值影响,正因如此,这也就保证了dp的无后效性。
(4)注意ans=max(anx,dp[i]),因为每个点都可能成为点权和最大的路径的终点
几个小细节
(1)为了方便,把缩点后都每个点编号从tot=n+1开始,这样就可以用原来的addline函数
(2)一定要在拓扑的同时dp
(3)我更喜欢在重新建图的过程中遍历m条边,其实遍历每个点的出边也可以,毕竟最开始每个边我们只建了一次,本题中一定满足不重不漏。
但是某些题目中可能会需要建双向边,这个时候就要注意不要把一条边建两次了,否则新建成的图会有环,而拓扑排序在一个有环的图里会死循环。
#include<bits/stdc++.h>
using namespace std;
const int maxm=3e5+5,maxn=3e4+5;
int n,m,head[maxm],ecnt=-1,sav[maxn];
int dfn[maxn],low[maxn],sum[maxn],tot,Time,top,col[maxn],indu[maxn],s[maxn],dp[maxn];
bool vis[maxn];
struct mint
{
int nxt,u,v;
}e[maxm];
inline void add(int u,int v)
{
e[++ecnt].nxt=head[u];
e[ecnt].u=u;
e[ecnt].v=v;
head[u]=ecnt;
}
void tarjan(int now)
{
dfn[now]=low[now]=++Time;
s[++top]=now;
for(int i=head[now];~i;i=e[i].nxt)
{
int v=e[i].v;
if(!dfn[v])
{
tarjan(v);
low[now]=min(low[now],low[v]);
}
else if(col[v]==0) low[now]=min(low[now],dfn[v]);
}
if(low[now]==dfn[now])
{
int cur;
tot++;
do
{
cur=s[top--];
col[cur]=tot;
sum[tot]+=sav[cur];
vis[cur]=false;
}while(cur!=now);
}
}
void topo()
{
queue<int> q;
for(int i=n+1;i<=tot;++i)
{
if(indu[i]==0) q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
dp[u]=max(dp[u],sum[u]);
for(int i=head[u];~i;i=e[i].nxt)
{
int v=e[i].v;
indu[v]--;
dp[v]=max(dp[v],dp[u]+sum[v]);
if(indu[v]==0) q.push(v);
}
}
}
int main()
{
memset(head,-1,sizeof(head));
int a,b;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&sav[i]);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&a,&b);
add(a,b);
}
tot=n;
for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
for(int i=0;i<=m-1;++i)
{
int a=e[i].u,b=e[i].v;
if(col[a]!=col[b])
{
indu[col[b]]++;
add(col[a],col[b]);
}
}
topo();
int ans=0;
for(int i=n+1;i<=tot;++i) ans=max(ans,dp[i]);
printf("%d",ans);
return 0;
}