P3387 【模板】缩点
P3387 【模板】缩点
题解
QWQ论这个题我开了多少数组QWQ
因为每个点走过多次权值只会计算1次
简化问题:把题目给出的有向图缩点,成为有向无环图,然后拓扑排序跑最长路
首先tarjan缩点
然后强连通分量连边
下面跑拓扑排序,入度为0的强连通分量 first
然后 dis[ ] 计算到达这个强连通分量时的最大权值(感觉这里有点像最长路QWQ)
最后取一个最大的 dis[ ]
拓扑:
把强连通分量连起来,然后拓扑排序寻找入度为0的点,更新dis[]
然后枚举出边,更新dis[],最后取最大
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<string> #include<cstring> #include<cstdlib> #include<queue> using namespace std; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int maxn=100010; int n,m,edge_num=0,qltedge_num=0,tim=0,top=0,qltnum=0; int head[maxn],qlthead[maxn],dfn[maxn],vis[maxn],low[maxn],cost[maxn]; int s[maxn],color[maxn],size[maxn],out[maxn],val[maxn],dis[maxn],in[maxn]; struct node { int nxt,to; }edge[maxn],qltedge[maxn]; void addedge1(int u,int v) { edge_num++; edge[edge_num].to =v; edge[edge_num].nxt =head[u]; head[u]=edge_num; } void addedge2(int u,int v) { qltedge_num++; qltedge[qltedge_num].to =v; qltedge[qltedge_num].nxt =qlthead[u]; qlthead[u]=qltedge_num; } void tarjan(int x) { dfn[x]=low[x]=++tim; vis[x]=1; s[++top]=x; for(int i=head[x];i;i=edge[i].nxt ) { int y=edge[i].to ; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(vis[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { qltnum++; int y; do { y=s[top--]; vis[y]=0; color[y]=qltnum; size[qltnum]++; val[qltnum]+=cost[y]; }while(x!=y); } } int topo() { queue<int> q; for(int i=1;i<=qltnum;i++) { if(!in[i]) q.push(i); dis[i]=val[i]; } while(!q.empty() ) { int x=q.front() ; q.pop() ; for(int i=qlthead[x];i;i=qltedge[i].nxt ) { int y=qltedge[i].to ; if(dis[y]<dis[x]+val[y]) dis[y]=dis[x]+val[y]; in[y]--; if(!in[y]) q.push(y); } } int ans=0; for(int i=1;i<=qltnum;i++) ans=max(ans,dis[i]); return ans; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) cost[i]=read(); int u,v; for(int i=1;i<=m;i++) { u=read();v=read(); addedge1(u,v); } for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i); } for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].nxt ) { if(color[i]!=color[edge[j].to ]) { addedge2(color[i],color[edge[j].to ]); in[color[edge[j].to ]]++; } } printf("%d\n",topo()); return 0; }