洛谷 P3387 【模板】缩点

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

 

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

 

输出格式:

 

共一行,最大的点权之和。

 

输入输出样例

输入样例#1: 复制
2 2
1 1
1 2
2 1
输出样例#1: 复制
2

说明

n<=10^4,m<=10^5,|点权|<=1000 算法:Tarjan缩点+DAGdp

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 100010
using namespace std;
queue<int>que;
map<int,int>ma[MAXN];
int n,m;
int sumcol,ans;
int tot,tot1,tim,top;
int col[MAXN],into[MAXN];
int val[MAXN],cost[MAXN],dis[MAXN];
int to[MAXN],net[MAXN],head[MAXN];
int to1[MAXN],net1[MAXN],head1[MAXN];
int low[MAXN],dfn[MAXN],vis[MAXN],stack[MAXN],visstack[MAXN];
void add(int u,int v){
    to[++tot]=v;net[tot]=head[u];head[u]=tot;
}
void add1(int u,int v){
    to1[++tot1]=v;net1[tot1]=head1[u];head1[u]=tot1;
}
void tarjin(int now){
    stack[++top]=now;
    low[now]=dfn[now]=++tim;
    vis[now]=1;
    visstack[now]=1;
    for(int i=head[now];i;i=net[i])
        if(visstack[to[i]])
            low[now]=min(low[now],dfn[to[i]]);
        else if(!vis[to[i]]){
            tarjin(to[i]);
            low[now]=min(low[now],low[to[i]]);
        }
    if(dfn[now]==low[now]){
        sumcol++;
        col[now]=sumcol;
        while(stack[top]!=now){
            col[stack[top]]=sumcol;
            visstack[stack[top]]=0;
            top--;
        }
        visstack[now]=0;
        top--;
    }
}
void spfa(int s){
    memset(dis,0,sizeof(dis));
    memset(vis,0,sizeof(vis));
    while(!que.empty())    que.pop();
    dis[s]=cost[s];
    vis[s]=1;
    que.push(s);
    while(!que.empty()){
        int now=que.front();
        que.pop();
        vis[now]=0;
        for(int i=head1[now];i;i=net1[i])
            if(dis[to1[i]]<dis[now]+cost[to1[i]]){
                dis[to1[i]]=dis[now]+cost[to1[i]];
                if(!vis[to1[i]]){
                    vis[to1[i]]=1;
                    que.push(to1[i]);
                }
            }
    }
    for(int i=1;i<=sumcol;i++)    ans=max(ans,dis[i]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)    scanf("%d",&val[i]);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i])
            tarjin(i);
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=net[j])
            if(col[i]!=col[to[j]])
                if(ma[col[i]].find(col[to[j]])==ma[col[i]].end()){
                    ma[col[i]][col[to[j]]]=1;
                    into[col[to[j]]]++;
                    add1(col[i],col[to[j]]);
                }
    for(int i=1;i<=n;i++)    cost[col[i]]+=val[i];
    for(int i=1;i<=sumcol;i++)
        if(!into[i])
            spfa(i);
    cout<<ans;
}

 

posted @ 2017-11-06 20:12  一蓑烟雨任生平  阅读(154)  评论(0编辑  收藏  举报