“华为杯“ 武汉大学21级新生程序设计竞赛 J.传闻档案

题目link

思路

反向建图,贪心跑bfs

反向建图权值从大到小跑bfs,来确定当前权值可以到达的点,在正常图(正向建边)中即是能到达它的点,然后开个 v i s vis vis数组记录一下点是否来过,贪心下每个点只更新一次,线性复杂度.

Code

const int inf = 0x3f3f3f3f;
const int INF = ~0ULL;
const int N = 1e6+10;
int n,m;


struct node{
    int id,w;
    bool operator<(const node&t) const{
        if(w==t.w) return id < t.id;
        return w > t.w;
    }
}a[100005];

vector<int>g[100005];
int in[100005];
int f[100005];
bool st[N];
map<int,int> mp;


void bfs(int u){
    queue<int> q;
    q.push(u);
    st[u] = 1;
    while(q.size()){
        int t = q.front();q.pop();
        for(auto i:g[t]){
            if(!st[i]){
                t = mp[i];
                a[t].w = max(a[t].w,a[mp[u]].w);
                st[i] = 1;
                q.push(i);
            }

        }

    
    }
}

signed main()
{
    cin>>n>>m;
    forr(i,1,n) cin >> a[i].w,a[i].id = i;
    while(m--){
        int u,v;
        cin>>u>>v;
        g[v].push_back(u);
    }

    sort(a+1,a+1+n);
    forr(i,1,n) mp[a[i].id] = i;
    forr(i,1,n){
        bfs(a[i].id);
    }

    int res = 0;
    forr(i,1,n) res += a[i].w;
    cout << res << endl; 
    return 0;
}

T a r j a n + d f s Tarjan+dfs Tarjan+dfs
t a r j a n tarjan tarjan算法缩完点之后直接跑一边 d f s dfs dfs,因为 t a r j a n tarjan tarjan之后强连通分量是逆拓扑序,直接倒着跑一遍 d f s dfs dfs更新缩完点之后每个点的最大权值

typedef long long ll;
int pri[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
const int inf = 0x3f3f3f3f;
const int INF = ~0ULL;
const int N = 1e6+10;
int n,m;
vector<int> g[100005],v[100005],scc[100005];
int dfn[100005],low[100005],id[100005];// id 结点在的强连通分量编号
int tim,cnt; // time 时间戳  cnt 连通分量个数
int stk[100005],top;
bool ins[100005];//是否在栈内
int num[100005];
int w[100005],val[100005];
bool st[100005];
int in[100005];
void tarjan(int u){
    dfn[u] = low[u] = ++tim;
    stk[++top] = u,ins[u] = 1;
    for(auto v:g[u]){
        if(!dfn[v]){
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(ins[v]) low[u] = min(low[u],low[v]);
    }
    if(low[u] == dfn[u]){
        cnt++;int y;
        do{
            y = stk[top--]; id[y] = cnt;
            ins[y] = 0;scc[cnt].push_back(y);
        }while(u != y);
    }
}

void dfs(int u){
    st[u] = 1;
    for(auto j:v[u]){
        if(!st[j]) dfs(j);
        val[u] = max(val[u],val[j]);
    }
}
signed main()
{
    cin >> n >> m;
    forr(i,1,n) cin >> w[i];
    while(m--){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
    }

    forr(i,1,n) if(!dfn[i]) tarjan(i);

    forr(i,1,n){
        for(auto j:g[i]){
            if(id[i] != id[j])
                v[id[i]].push_back(id[j]),in[id[j]]++;
        }
    }

    // forr(i,1,cnt){        
    //     cout << i <<" ";
    //     for(auto j:v[i]) cout << j <<" ";
    //     cout << endl;
    // }

    forr(i,1,cnt){
        int res = 0;
        for(auto j:scc[i]){
            res = max(res,w[j]);
        }
        val[i] = res;
    }
    
    for(int i = cnt;i;i--) if(!st[i]) dfs(i);      
    // forr(i,1,cnt) cout << val[i] << endl; 
    int res = 0;
    forr(i,1,cnt) res += scc[i].size()*val[i];
    cout << res << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值