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;
}
时间: 2024-11-05 12:34:58

Codeforces #447 Div2 E的相关文章

[2016-04-13][codeforces][447][C][DZY Loves Sequences]

时间:2016-04-13 23:39:47 星期三 题目编号:[2016-04-13][codeforces][447][C][DZY Loves Sequences] 题目大意:给定一串数字,问改变其中一个数字之和,最长能得到多长的严格增加的子串 分析: 维护每个数字往左和往右能延续多长(严格减,增),然后枚举每个点, 如果这个点已经在一个严格增加的序列中,那么ans =min(n, max(ans , l[i] + r[i] + 1)) 即左右两边延伸之后,改变后面非递增的一个数字 注意这

[2016-04-13][codeforces][447][B][DZY Loves Strings]

时间:2016-04-13 23:36:46 星期三 题目编号:[2016-04-13][codeforces][447][B][DZY Loves Strings] 题目大意:已知每个字母的权值,给定一个字符串s,问,插入k个字母进入字符串s之和,最多能得到多大的权值 分析:直接往s后面插入k个权值最大的字母 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; char

Codeforces 583 DIV2 Robot&#39;s Task 贪心

原题链接:http://codeforces.com/problemset/problem/583/B 题意: 就..要打开一个电脑,必须至少先打开其他若干电脑,每次转向有个花费,让你设计一个序列,使得总花费最小. 题解: 就傻傻的走就好..从左走到右,再走回来,更新序列和答案就好. 代码: #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #define MA

Codeforces #180 div2 C Parity Game

// Codeforces #180 div2 C Parity Game // // 这道题的题目意思就不解释了 // // 题目有那么一点难(对于我而言),不多说啦 // // 解题思路: // // 首先如果a串和b串相等,不多说直接YES // 如果b串全是0,直接YES // 注意到a串有一个性质,1的个数不会超过本身的加1. // a有个1的上限设为x,b有个1的个数设为y,则如果x < y // 那么直接NO. // // 现在一般情况下,就是模拟啦,找到a的后缀和b的前缀一样的

[2016-04-13][codeforces][447][A][DZY Loves Hash]

[2016-04-13][codeforces][447][A][DZY Loves Hash].md 时间:2016-04-13 23:35:11 星期三 题目编号:[2016-04-13][codeforces][447][A][DZY Loves Hash] 题目大意:问hash是否冲突 分析:模拟一遍即可 #include<cstdio> #include<cstring> using namespace std; const int maxn = 300 + 10; in

Codeforces #246(div2)

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <ti

Codeforces #245(div2)

A:A. Points and Segments (easy) 题目看了n久,开始觉得尼玛这是div2的题目么,题目还标明了easy.. 意思是给你一n个点,m个区间,在n个点上放蓝球或者红球,然后让你找一种选择方案使得m个区间内的蓝球和红球数量之差不超过1. 开始想过用dfs,不过这只是div2的A题而已.. 然后想了下,直接输出010101序列不就可以么. 交了一发,发现要先排个序,再输出就可以了. AC代码: #include<iostream> #include<cstdio&g

codeforces#327 div2

codeforces#327 div2 这场状态不好有点可惜,题目都不难,而且很好.. A题:水题. #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; typedef lo

codeforces#FF(div2) DZY Loves Sequences

n个数,可以任意改变其中一个数,求最长的上升子区间长度 思路:记录一个from[i]表示从位置i的数开始最长的上升区间长度 记录一个to[i]表示到位置i的数所能达到的最长上升区间长度 枚举要改变的数的位置i,此时能达到的长度为to[i - 1] + from[i + 1] + 1,取最大值 //#pragma comment(linker, "/STACK:102400000,102400000") //HEAD #include <cstdio> #include &l