最大点权 ( 强连通分量 )

最大点权

Time Limit: 3000/1000 MS (Java/Others)
Memory Limit: 65535/100000 K (Java/Others)

Problem Description
给定一个有向图,每个点i有点权ai,请对于每个点i,找到i能到达的点中点权的最大值(包括i点)。
Input
第一行包含一个正整数 T T T ( 1 ≤ T ≤ 10 ) (1≤T≤10) (1T10),表示测试数据的组数。

每组数据第一行包含两个正整数 n , m n,m n,m ( 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 200000 ) (1≤n≤100000,1≤m≤200000) (1n100000,1m200000),表示点数和边数。

第二行包含 n n n个正整数 a 1 , a 2 , … , a n ( 1 ≤ a i ≤ 1 0 9 ) a1,a2,…,an(1≤a_i≤10^9) a1,a2,,an(1ai109),依次表示每个点的点权。

接下来 m m m行,每行包含两个正整数 u i , v i ( 1 ≤ u i , v i ≤ n , u i ≠ v i ) u_i,v_i(1≤u_i,v_i≤n,u_i≠v_i) ui,vi(1ui,vin,ui=vi),表示一条 u i → v i u_i→v_i uivi的单向边。
Output
对于每组数据输出n行,每行一个整数,第i行的数表示i点能到达的点中点权的最大值。
Sample Input

1
6 6
3 7 5 3 8 5
1 2
2 3
3 1
4 5
5 6
2 6

Sample Output

7
7
7
8
8
5

强连通分量 + 记忆化搜索

#include <bits/stdc++.h>
using namespace std;
#define MAIN signed main()
#define IOS std::ios::sync_with_stdio(false)
#define pb push_back
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
const int maxn = 1e5+5;
int w[maxn];//weight
vi g1[maxn], g2[maxn],g3[maxn]; //正向图 ,反向图 ,缩点图
int q[maxn], idx;//pop stack sequence 
bool vis[maxn];
int p[maxn]; //set
int dp[maxn], mx[maxn];

void dfs1(int x)
{
    vis[x] = true;
    int len = g1[x].size();
    for(int i=0;i<len;++i) if(!vis[g1[x][i]]) dfs1(g1[x][i]);
    q[++idx] = x;
}

void dfs2(int x,int father)
{
    vis[x] = false;
    p[x] = father;
    mx[father] = max(w[x], mx[father]);
    int len = g2[x].size();
    for(int i=0;i<len;++i) if(vis[g2[x][i]]) dfs2(g2[x][i],father);
}

int dfs(int x) //记忆化搜索 
{
    if(dp[x]) return dp[x];
    dp[x] = mx[x];
    int len = g3[x].size();
    for(int i=0;i<len;++i)
    {
        dp[x] = max(dp[x], dfs(g3[x][i]));
    }
    return dp[x];
}

void solve()
{
    int n ,m, a, b;
    scanf("%d%d",&n,&m);
    memset(q,0,sizeof q);
    memset(vis,0,sizeof vis);
    memset(mx,0,sizeof mx);
    memset(dp,0,sizeof dp);
    memset(p,0,sizeof p);
    idx = 0;
    
    for(int i=1;i<=n;++i) 
    {
        scanf("%d",&w[i]);
    }
    while(m--)
    {
        scanf("%d%d",&a,&b);
        g1[a].pb(b);
		g2[b].pb(a);
    }
    for(int i=1;i<=n;++i) if(!vis[i]) dfs1(i);
    for(int i=n;i;--i    ) if(vis[q[i]]) dfs2(q[i],q[i]);
    for(int i=1;i<=n;++i)
    {
        int len = g1[i].size();
        for(int j=0;j<len;++j)
        {
            if(p[i] == p[g1[i][j]]) continue;
            g3[p[i]].pb(p[g1[i][j]]);
        }
    }
    for(int i=1;i<=n;++i)
    {
//        int len = g3[i].size();
//        printf("%d->",i);
//        for(int j=0;j<len;++j)
//        {
//            printf("%d ",g3[i][j]);
//        }
//        printf("\n");
//        printf("%d\n",mx[i]);
        printf("%d\n",dfs(p[i]));
    }
    for(int i=1;i<=n;++i) g1[i].clear(), g2[i].clear(), g3[i].clear();
}


MAIN
{
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;++i)
    {
        solve();
    }

    return 0;
}

/*
2
6 6
3 7 5 3 8 5
1 2
2 3
3 1
4 5
5 6
2 6
6 7
3 7 5 3 8 5
1 2
2 3
3 1
4 5
5 6
2 6
6 4
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值