/*
luogu P3387
缩点(强连通分量)+DAGdp
by sbn
2018-2-11
algorithm:Kosaraju
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
const int MAXN=1e5+11;
struct node{
int x,y,nxt;
node(){}
node(int nx,int ny,int nnxt){
x=nx;y=ny;nxt=nnxt;
}
}E[MAXN*4],F[MAXN*4],S[MAXN*4];
// E:原图 F:逆向图 S:缩点后的图
int cnt,head[MAXN],headf[MAXN],cntf,cnts,heads[MAXN],dp[MAXN];
//cnt?和head?配套图使用
//dp表示从起点到这个点最大值
int rec[MAXN],size,rd[MAXN]={0},w[MAXN],cd[MAXN];
//rec是dfs逆向图生成的dfs序列(见Kosaraju算法)
//size是序列大小
//rd,cd是缩点后各点的入度,出度
//w是原图中各个点的权值
int ds[MAXN];
//ds是缩点后各个强连通分量的各点权值之和
bool vis[MAXN];
//各种搜索用的标记
inline void link(int x,int y){
E[++cnt]=node(x,y,head[x]);
head[x]=cnt;
F[++cntf]=node(y,x,headf[y]);
headf[y]=cntf;
}
int n,m,a,b,ans[MAXN],len[MAXN],q,num,ptd[MAXN];
//n是点的个数,m是边的个数,a,b是用于读入时的点
/*len,ans用于存生成的缩点
例如ans:1,2,3,4,5,6
len:0,3,6
表示123,456是两强连通分量
*/
// q是dfs时的临时操作,表示ans下标
//num是强连通分量的个数
//ptd是每个点对应的强连通分量是谁
void dfsf(int u){
vis[u]=1;
for (int i=headf[u];i;i=F[i].nxt)
if (!vis[F[i].y]) dfsf(F[i].y);
size++;
rec[size]=u;
return;
}
//dfs逆序图,生成rec
void dfs(int u){
vis[u]=1;
q++;
ans[q]=u;
ptd[u]=num;
for (int i=head[u];i;i=E[i].nxt)
if (!vis[E[i].y])
dfs(E[i].y);
return;
}
//dfs正序图,生成ans,len
inline void link_D(int x,int y){
S[++cnts]=node(x,y,heads[x]);
heads[x]=cnts;
}
//缩点后的链接
inline void build(){
for(int i=1;i<=n;i++)
for (int j=head[i];j;j=E[j].nxt)
if (ptd[i]!=ptd[E[j].y])
{
link_D(ptd[i],ptd[E[j].y]);
rd[ptd[E[j].y]]++;
cd[ptd[i]]++;
}
}
//缩点后建图
void bfs(int u){
queue<int> Q;
Q.push(u);
while (!Q.empty()){
int k=Q.front();
Q.pop();
for (int i=heads[k];i;i=S[i].nxt)
{
dp[S[i].y]=max(dp[S[i].y],dp[k]+ds[S[i].y]);
Q.push(S[i].y);
}
}
}
//DAGdp部分,宽搜记忆化搜索
int main(){
// freopen("qltfl.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
for (int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
link(a,b);
}
memset(vis,0,sizeof(vis));
for (int j=1;j<=n;j++)
if (!vis[j]) dfsf(j);//生成rec
memset(vis,0,sizeof(vis));
for (int j=size;j>=1;j--)
if (!vis[rec[j]]) {
num++;
len[num]=q;
dfs(rec[j]);
} //缩点完毕
//cout<<num<<endl;
len[num+1]=n;
for (int i=1;i<=num;i++)
for (int j=len[i]+1;j<=len[i+1];j++)
ds[i]+=w[ans[j]];
build(); //统计个缩点的权值和建图
for (int i=1;i<=num;i++)
dp[i]=ds[i];
memset(vis,0,sizeof(vis));
for (int j=1;j<=num;j++)
if (rd[j]==0)
bfs(j); //dp
int maxn=0;
for (int i=1;i<=num;i++)
maxn=max(dp[i],maxn);
cout<<maxn<<endl;
return 0;
}
强连通分量模板+DAGdp 洛谷P3387
最新推荐文章于 2022-03-17 21:01:39 发布