zoukankan      html  css  js  c++  java
  • [codeforces 894 E] Ralph and Mushrooms 解题报告 (SCC+拓扑排序+DP)

    题目链接:http://codeforces.com/problemset/problem/894/E

    题目大意:

    $n$个点$m$条边的有向图,每条边有一个权值,可以重复走。

    第$i$次走过某条边权为$w$的边后这条边的边权变成$w-i$,但不能小于等于$0$。

    给定起点,询问任意走最多能获得多少的边权

    题解:

    显然一个强联通分量的边可以全部走到$0$为止。

    考虑强连通分量中一条边权为w的边对答案的贡献,$s=w+w-1+w-1-2+w-1-2-3ldots$

    设这个式子有$t+1$项,显然有$frac{(t+1)t}{2}<=w$,解得$t=sqrt{2w+0.25}-0.5$,t下取整

    那么$s=(t+1)w-frac{(t+2)(t+1)t}{6}$

    得到每个强连通分量之后剩下的部分拓扑排序后DP即可

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<queue>
    #include<cmath>
    typedef long long ll;
    using namespace std;
    
    const int N=1e6+1000;
    const int MAX=1e8+15;
    const ll inf=1ll<<60;//这个要足够大,卡了2h... 
    int n,m,tot,top,tim,scc,t;
    int sta[N],dfn[N],low[N],belong[N],deg[N],head[N];
    bool ins[N];
    ll dp[N],val[N],sum[N],tmp[N];
    struct EDGE
    {
        int from,to,nxt;ll w;
    }edge[N];
    struct node{int v;ll w;};
    vector <node> g[N];
    inline ll read()
    {
        char ch=getchar();
        ll s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();};
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void add(int u,int v,int w)
    {
        edge[++tot]=(EDGE){u,v,head[u],w};
        head[u]=tot; 
    }
    void tarjan(int x)
    {
        dfn[x]=low[x]=++tim;
        ins[x]=1;sta[++top]=x;
        for (int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if (!dfn[y])
            {
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if (ins[y]) low[x]=min(low[x],dfn[y]);
        }
        if (dfn[x]==low[x])
        {
            ++scc;int k;
            do
            {
                k=sta[top--];
                belong[k]=scc;
                ins[k]=0;
            }
            while(k!=x);
        }
    }
    inline ll calc(ll x)
    {
        ll tt=sqrt(2*x+0.25)-0.5;
        return x+tt*x-(tt+1)*(tt+2)*tt/6;
    }
    int main()
    {
        //for (t=1;tmp[t-1]<MAX;t++) {tmp[t]=tmp[t-1]+t;sum[t]=sum[t-1]+tmp[t];}
        n=read();m=read();
        for (int i=1,u,v,w;i<=m;i++)
        {
            u=read();v=read();w=read();
            add(u,v,w);
        }
        for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
        for (int i=1;i<=m;i++)
        {
            int u=edge[i].from,v=edge[i].to;
            u=belong[u],v=belong[v];
            if (u==v) val[u]+=calc(edge[i].w);
        }
        for (int u=1;u<=n;u++)
        {
            for (int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                int bu=belong[u],bv=belong[v];
                if (bu==bv) continue;
                g[bu].push_back((node){bv,edge[i].w+val[bv]});
                deg[bv]++;
            }
        }
        int st=read();st=belong[st];
        queue <int> q;
        for (int i=1;i<=scc;i++) dp[i]=-inf;dp[st]=val[st];
        for (int i=1;i<=scc;i++) if (!deg[i]) q.push(i);
        while (!q.empty()){
            int k=q.front();q.pop();
            for (int i=0;i<g[k].size();i++){
                int y=g[k][i].v;
                if (--deg[y]==0) q.push(y);
                dp[y]=max(dp[y],dp[k]+g[k][i].w);
            }
        }
        ll mx=0;
        for (int i=1;i<=scc;i++) mx=max(mx,dp[i]);
        printf("%lld
    ",mx);
        return 0;
    }
  • 相关阅读:
    20190127-将一个文件拆分为多个新文件
    20190125-找到列表第二大的数以及自己写一个冒泡排序
    20190121-n个人围成一圈,凡报到3的人退出圈子,最后留下的是原来第几号的那位
    20190120-自定义实现split方法
    20190118-自定义实现replace方法
    20190118-利用Python实现Pig Latin游戏
    20190116-将特定数字插入一个已经排序好的序列并且不改变其排序规则
    20190112-自定义实现字符串的操作方法,如strip,upper,title,ljust,center,zfill,find,rfind等
    20190110-用笨办法找到二维矩阵的鞍点
    我想转行—程序员转行自媒体
  • 原文地址:https://www.cnblogs.com/xxzh/p/9762034.html
Copyright © 2011-2022 走看看