题意:给一个有向图,n个点m条边,每个点有点权xi。规定从u点出发,到指定的k个点之一结束,可以多次经过同一个点和同一条边,求路径上点权和的最大值。

题解:直接缩点变成DAG,然后dp的时候并不是直接往父亲上面加,比如下面的4个点4条边的图:

4 4
1 2
1 3
2 4
3 4

直接往父亲上面加会导致x[4]出现了两次。

注意到这个其实是DAG,拓扑的时候把自己的值尝试更新给父亲,父亲保留所有儿子之中的最大值就可以了。

又交又WA,忘记考虑结束点的情况了。只有当这个儿子的答案中携带结束点的时候才往父亲更新,同时设置父亲的答案也是携带有结束点。

又交又T,板子里有个排序去重,5e5居然不能nlogn,好吧,それは私の過ちです。

namespace SCC {
    const int MAXN = 5e5;

    int n;
    vector<int> G[MAXN + 5], BG[MAXN + 5];

    int c1[MAXN + 5], cntc1;
    int c2[MAXN + 5], cntc2;
    int s[MAXN + 5], cnts;

    int n2;
    vector<int> V2[MAXN + 5];
    vector<int> G2[MAXN + 5], BG2[MAXN + 5];

    int val[MAXN + 5];
    ll val2[MAXN + 5];
    bool ok[MAXN + 5];
    bool ok2[MAXN + 5];

    void Init(int _n) {
        n = _n;
        cntc1 = 0, cntc2 = 0, cnts = 0;
        for(int i = 1; i <= n; ++i) {
            G[i].clear();
            BG[i].clear();
            c1[i] = 0;
            c2[i] = 0;
            s[i] = 0;
            V2[i].clear();
            G2[i].clear();
            BG2[i].clear();
            val[i] = 0;
            val2[i] = 0;
            ok[i] = 0;
            ok2[i] = 0;
        }
    }

    void AddEdge(int u, int v) {
        G[u].push_back(v);
        BG[v].push_back(u);
    }

    void dfs1(int u) {
        c1[u] = cntc1;
        for(int v : G[u]) {
            if(!c1[v])
                dfs1(v);
        }
        s[++cnts] = u;
    }

    void dfs2(int u) {
        V2[cntc2].push_back(u);
        val2[cntc2] += val[u];
        ok2[cntc2] |= ok[u];
        c2[u] = cntc2;
        for(int v : BG[u]) {
            if(!c2[v])
                dfs2(v);
        }
    }

    void Kosaraju() {
        for(int i = 1; i <= n; ++i) {
            if(!c1[i]) {
                ++cntc1;
                dfs1(i);
            }
        }

        for(int i = n; i >= 1; --i) {
            if(!c2[s[i]]) {
                ++cntc2;
                dfs2(s[i]);
            }
        }
    }

    void Build() {
        n2 = cntc2;
        for(int i = 1; i <= n2; ++i) {
            for(auto u : V2[i]) {
                for(auto v : G[u]) {
                    if(c2[v] != i) {
                        G2[i].push_back(c2[v]);
                        BG2[c2[v]].push_back(i);
                    }
                }
            }
        }

        for(int i = 1; i <= n2; ++i) {
            //sort(G2[i].begin(), G2[i].end());
            //G2[i].erase(unique(G2[i].begin(), G2[i].end()), G2[i].end());
            //sort(BG2[i].begin(), BG2[i].end());
            //BG2[i].erase(unique(BG2[i].begin(), BG2[i].end()), BG2[i].end());
        }
    }


    queue<int> Q;
    int outdeg[MAXN + 5];
    ll dp[MAXN + 5];
    bool ok3[MAXN + 5];
    void Solve(int u) {
        for(int i = 1; i <= n2; ++i) {
            if(G2[i].size() == 0)
                Q.push(i);
            outdeg[i] = G2[i].size();
            dp[i] = 0;
            ok3[i] = ok2[i];
        }
        while(!Q.empty()) {
            int u = Q.front();
            Q.pop();
            if(ok3[u])
                dp[u] += val2[u];
            for(auto v : BG2[u]) {
                if(ok3[u]) {
                    dp[v] = max(dp[v], dp[u]);
                    ok3[v] = 1;
                }
                outdeg[v]--;
                if(outdeg[v] == 0)
                    Q.push(v);
            }
        }
        u = c2[u];
        printf("%lld\n", dp[u]);
    }
}

void test_case() {
    int n, m;
    scanf("%d%d", &n, &m);
    SCC::Init(n);
    while(m--) {
        int u, v;
        scanf("%d%d", &u, &v);
        SCC::AddEdge(u, v);
    }
    for(int i = 1; i <= n; ++i)
        scanf("%d", &SCC::val[i]);
    int u, k;
    scanf("%d%d", &u, &k);
    while(k--) {
        int v;
        scanf("%d", &v);
        SCC::ok[v] = 1;
    }
    SCC::Kosaraju();
    SCC::Build();
    SCC::Solve(u);
}
12-24 07:58