CodeForces - 894E Ralph and Mushrooms (强连通缩点+dp)

题意:一张有向图,每条边上都有wi个蘑菇,第i次经过这条边能够采到w-(i-1)*i/2个蘑菇,直到它为0。问最多能在这张图上采多少个蘑菇。

分析:在一个强连通分量内,边可以无限次地走直到该连通块内蘑菇被采完为止,因此每个强连通分量内的结果是确定的。

设一条边权值为w,最大走过次数为t,解一元二次方程得 t = (int)(1+sqrt(1+8w));则该边对所在连通块的贡献为w*t - (t-1)*t*(t+1)/6。

而不在任何一个强连通分量内的边,最多只能走一次。所以在缩点后的DAG上进行dp即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn =1e6+5;
struct Edge{
    int v,next;
    LL val;  
}edges[maxn],E[maxn];
int head[maxn],tot,H[maxn],tt;
stack<int> S;
int pre[maxn],low[maxn],sccno[maxn],dfn,scc_cnt;
LL W[maxn];
LL dp[maxn];
void init()
{
    tot = dfn = scc_cnt=tt=0;
    memset(H,-1,sizeof(H));
    memset(W,0,sizeof(W));
    memset(dp,0,sizeof(dp));
    memset(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    memset(head,-1,sizeof(head));
}

void AddEdge(int u,int v,LL val)   {
    edges[tot] = (Edge){v,head[u],val};
    head[u] = tot++;
}

void Tarjan(int u)
{
    int v;
    pre[u]=low[u]=++dfn;
    S.push(u);
    for(int i=head[u];~i;i=edges[i].next){
        v= edges[i].v;
        if(!pre[v]){
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v]){
            low[u]=min(low[u],pre[v]);
        }
    }
    if(pre[u]==low[u]){
        int x;
        ++scc_cnt;
        for(;;){
            x = S.top();S.pop();
            sccno[x]=scc_cnt;
            if(x==u)break;
        }
    }    
}


void nAddEdge(int u,int v,LL w)
{
    E[tt] = (Edge){v,H[u],w};
    H[u] = tt++;
}

LL dfs(int u)
{
    if(dp[u]) return dp[u];
    for(int i=H[u];~i;i=E[i].next){
        int v = E[i].v;
        dp[u] = max(dp[u],dfs(v)+E[i].val);
    }
    dp[u]+=W[u];
    return dp[u];
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int N,M; 
    while(scanf("%d%d",&N,&M)==2){
        init();
        int st,u,v; LL w;
        while(M--){
            scanf("%d%d%lld",&u,&v,&w);
            AddEdge(u,v,w);
        }
        scanf("%d",&st);
        for(int i=1;i<=N;++i){
            if(!pre[i]){
                Tarjan(i);
            }
        }
        
        for(int u =1;u<=N;++u){
            for(int i =head[u];~i;i = edges[i].next){
                v = edges[i].v;
                LL w = edges[i].val;
                if(sccno[u]!=sccno[v]){
                    nAddEdge(sccno[u],sccno[v],w);
                }
                else{
                    int t = (int)(1+sqrt(1+8*w))/2;
                    W[sccno[u]] += (LL)t*w - (LL)(t-1)*t*(t+1)/6;
                }
            }
        }
        for(int i=1;i<=scc_cnt;++i){
            if(!dp[i]){
                dfs(i);
            }
        }
        printf("%lld\n",dp[sccno[st]]);
    }
    return 0;
}

 

posted @ 2018-08-17 19:05  xiuwenL  阅读(272)  评论(0编辑  收藏  举报