洛谷P4742(tarjan缩点+拓扑DP)

时间:2022-12-31 14:20:39

https://www.luogu.org/problemnew/show/P4742

题目背景

[Night - 20:02[Night20:0P.M.]P.M.]

夜空真美啊……但是……快要结束了呢……

题目描述

一天的活动过后,所有学生都停下来欣赏夜空下点亮的风筝。CurtisCurtiNishikinoNishikino想要以更近的视角感受一下,所以她跑到空中的风筝上去了(这对于一个妹子来说有点匪夷所思)! 每只风筝上的灯光都有一个亮度 k_iki. 由于风的作用,一些风筝缠在了一起。但是这并不会破坏美妙的气氛,缠在一起的风筝会将灯光汇聚起来,形成更亮的光源!

CurtisCurtiNishikinoNishikino已经知道了一些风筝间的关系,比如给出一对风筝(a,b)(a,b), 这意味着她可以从 aa 跑到 bb上去,但是不能返回。

现在,请帮她找到一条路径(她可以到达一只风筝多次,但只在第一次到达时她会去感受上面的灯光), 使得她可以感受到最多的光亮。同时请告诉她这条路径上单只风筝的最大亮度,如果有多条符合条件的路径,输出能产生最大单只风筝亮度的答案。

输入输出格式

输入格式:

 

第一行两个整数 nn 和 mm. nn 是风筝的数量, mm 是风筝间关系对的数量.

接下来一行 nn 个整数 k_iki.

接下来 mm 行, 每行两个整数 aa 和 bb, 即CurtisCurtis可以从 aa 跑到 bb.

 

输出格式:

 

一行两个整数。CurtisCurtis在计算出的路径上感受到的亮度和,这条路径上的单只风筝最大亮度.

 

输入输出样例

输入样例#1:  复制
5 5
8 9 11 6 7
1 2
2 3
2 4
4 5
5 2
输出样例#1:  复制
41 11

说明

对于 20\%20% 的数据, 0<n \le 5\times10^3, \ 0 < m \le 10^40<n5×103, 0<m104.

对于 80\%80% 的数据, 0 < n \le 10^5, \ 0 < m \le 3\times10^50<n105, 0<m3×105.

对于 100\%100% 的数据, 0<n\le2\times10^5,\ 0<m\le5\times10^5,\ 0<k\le2000<n2×105, 0<m5×105, 0<k200.

思路:tarjan缩点+拓扑DP,其实DP就是最短路松弛操作。

///缩点+拓扑DP
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e5+4;
int n,m,cnt,color;
int a[maxn],DFN[maxn],LOW[maxn],belong[maxn],dis[maxn],maxdis[maxn],maxval[maxn],in[maxn],sumval[maxn];
bool vis[maxn];
stack<int>s;
vector <int> G[maxn],NG[maxn];
void tarjan(int x){
    DFN[x]=LOW[x]=++cnt;
    vis[x]=true;
    s.push(x);
    for(int i=0;i<G[x].size();i++){
        int to=G[x][i];
        if(!DFN[to]){
            tarjan(to);
            LOW[x]=min(LOW[x],LOW[to]);
        }else if(vis[to]){
            LOW[x]=min(LOW[x],DFN[to]);
        }
    }
    if(LOW[x]==DFN[x]){
        int temp;
        color++;
        maxval[color]=0;
        do{
            temp=s.top();
            s.pop();
            vis[temp]=false;
            belong[temp]=color;
            sumval[color]+=a[temp];
            maxval[color]=max(maxval[color],a[temp]);
        }while(temp!=x);
    }
}
///dis[x]/以x为终点的最大距离和,maxdis[x]/以x为终点的最大距离间隔
void tpu(){
    fill(dis,dis+color+1,-INF);
    queue<int>Q;
    for(int i=1;i<=color;i++){
        if(in[i]==0){
            dis[i]=sumval[i];
            maxdis[i]=maxval[i];
            Q.push(i);
        }
    }
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        for(int i=0;i<NG[u].size();i++){
            int to=NG[u][i];
            in[to]--;
            if(dis[to]<dis[u]+sumval[to]){///如果从u-》to的价值和更加大,就更新,而且要更新maxdis,为maxval[to]和之前dis[u]的更大的那个
                dis[to]=dis[u]+sumval[to];
                maxdis[to]=max(maxdis[u],maxval[to]);
            }else if(dis[to]==dis[u]+sumval[to]){///如果相等,就要考虑maxdis的更新
                maxdis[to]=max(maxdis[to],maxdis[u]);
            }
            if(in[to]==0){
                Q.push(to);
            }
        }
    }
    int anssum=0,ansmax=0;
    for(int i=1;i<=color;i++){
        if(anssum<dis[i]){
            anssum=dis[i];
            ansmax=maxdis[i];
        }else if(anssum==dis[i]){
            ansmax=max(ansmax,maxdis[i]);
        }
    }
    printf("%d %d\n",anssum,ansmax);
}
int main(){
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=0;i<m;i++){
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
    }
    for(int i=1;i<=n;i++){
        if(!DFN[i]){
            tarjan(i);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<G[i].size();j++){
            int to=G[i][j];
            if(belong[i]!=belong[to]){
                NG[belong[i]].push_back(belong[to]);
                in[belong[to]]++;
            }
        }
    }
    tpu();
    return 0;
}