Codeforces #447 Div2 E

#447 Div2 E

题意

给出一个由有向边构成的图,每条边上有蘑菇,假设有 \(n\) 个蘑菇,那么第一次走过这条边可以获得 \(n\) 个蘑菇,第二次 \(n-1\),第三次 \(n-1-2\),第四次 \(n-1-2-3\),后面类推,直至为 \(0\)。问从选定点出发最多可以获得几个蘑菇。

分析

Tarjan 算法缩点,重新给点标号(缩点),且保证了拓扑排序中靠后的点先标号,对于缩完点后的有向无环图,DP去求最长路。(对于拓扑排序后的序列,根据拓扑排序的性质,可以从后往前DP)
拓扑排序保证了:对于有向边 \(a-b\)\(a\) 一定在 \(b\) 前面。

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
struct Edge {
    int v, w, nxt;
}e[N];
int head[N], cnt;
void addEdge(int u, int v, int w) {
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
}
int n, m, c, nn, vis[N], dfn[N], low[N];
int f[N]; // 被缩成的新点的序号
ll sup[N]; // 这个新点能提供的贡献
stack<int> sta;
vector<int> G[N];
void tarjan(int u) { // 找强连通分量
    sta.push(u);
    dfn[u] = low[u] = ++c;
    vis[u] = 1;
    for(int i = head[u]; ~i; i = e[i].nxt) {
        int v = e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(vis[v] && low[u] > dfn[v]) {
            low[u] = dfn[v];
        }
    }
    if(low[u] == dfn[u]) {
        ++nn;
        while(1) {
            int id = sta.top();
            G[nn].push_back(id);
            f[id] = nn;
            sta.pop();
            vis[id] = 0;
            if(id == u) break;
        }
    }
}
ll calc(int w) {
    int d = sqrt(2 * w);
    while(d * d + d > 2 * w) d--;
    return 1LL * w * (d + 1) - (1LL * d * (d + 1) * (2 * d + 1) / 6 + d * (d + 1) / 2) / 2;
}
ll dp[N];
int main() {
    memset(head, -1, sizeof head);
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addEdge(u, v, w);
    }
    nn = n;
    for(int i = 1; i <= n; i++) {
        if(!dfn[i]) tarjan(i);
    }
    // 计算每个强连通分量缩成的点能提供的贡献
    for(int i = 1; i <= n; i++) {
        for(int j = head[i]; ~j; j = e[j].nxt) {
            int v = e[j].v;
            if(f[i] == f[v]) sup[f[i]] += calc(e[j].w);
        }
    }
    int s;
    scanf("%d", &s);
    s = f[s];
    for(int i = n + 1; i <= nn; i++) {
        for(int j = 0; j < G[i].size(); j++) {
            int q = G[i][j];
            for(int p = head[q]; ~p; p = e[p].nxt) {
                int v = e[p].v;
                if(f[q] != f[v])
                    dp[i] = max(dp[i], dp[f[v]] + sup[f[v]] + e[p].w);
            }
        }
    }
    cout << sup[s] + dp[s] << endl;
    return 0;
}
posted @ 2017-11-22 22:10  ftae  阅读(226)  评论(0编辑  收藏  举报