zoukankan      html  css  js  c++  java
  • [Luogu P3953] 逛公园 (最短路+拓扑排序+DP)

    题面

    传送门:https://www.luogu.org/problemnew/show/P3953


    Solution

    这是一道神题

    首先,我们不妨想一下K=0,即求最短路方案数的部分分。

    我们很容易可以想到一个做法,就是魔改迪杰斯特拉做法:

    如果一个点可以更新到达其他点的距离,那个点的方案数就是这个点的方案数;如果一个点所更新出来的距离和之前的相等,那个点的方案数加等当前点的方案数。

    用式子可以表现为:

    f[j]=f[i] (dis[j]>dis[i]+x)  

    f[j]+=f[i] (dis[j]==dis[i]+x)

    (i表示当前点,j表示它更新的点,x为i到j那条路的距离)

    那我们怎么保证它的顺序不会出错,即如何保证一个点去更新其他点的方案数的时候,这个点的方案数是正确的呢?

    事实上,这种做法就是一种DP。

    那么,对于K!=0的情况怎么处理呢?

    观察数据,我们会发现K最大只有50。

    因此,我们可以考虑在DP上加一维来解决这个K值。

    考虑这样设状态:

    f[i][j] 表示到达i点,距离为dis[i]+j 的方案数

    转移非常好写

    f[i][j] = sigema (f[k][dis[i]+j-dis[k]-a]) (k为直接连到i的点,a表示它们之间的边权)

    初始化其实我们在30分做法中就已经求好了。

    转移顺序是个问题。

    我们显然可以在外层枚举j,问题是,有时候,dis[i]+j-dis[k]-a会等于j,如果枚举i的顺序错了,答案肯定会跟着错。

    对于dis[i]+j-dis[k]-a==j 的点,肯定是k去更新i,又因为边权没有负值,所以我们就可以按照dis从小到大去去枚举i的值。

    以上是没有零边的做法。

    对于有零边的情况,我们刚刚的做法就会出问题。如图:

    所以说,我们把原图转换一下,只保留0边,对新图做拓扑排序。

    如果做完拓扑排序之后,有几个点没有进入过排序中,就说明这个图有零环,就gg了。

    我们把拓扑序做完之后再执行原来有的最短路和dp,这样就不会错了。

    就酱,我们就嘴巴AC这道题啦o(* ̄▽ ̄*)o 。

    事实上,这样做并A不了,因为这题TM卡常(╯°Д°)╯︵┻━┻

    然后,你会被卡30分并因此退役(或者是开O2A掉这道题(但是NOIP中并不开O2,所以你还是因此退役了))


    Code

    //Luogu P3953 逛公园
    //Sep,18th,2018
    //最短路+拓扑排序+DP+卡常神题
    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    long long read()
    {
        long long x=0,f=1; char c=getchar();
        while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    const int N=100000+100;
    const int M=50+5;
    const int inf=0x3f3f3f3f;
    struct DIS
    {
        int dis,no,zero;
    }dis[N];
    bool cmp(DIS A,DIS B)
    {
        if(A.dis==B.dis)
            return A.zero<B.zero;
        return A.dis<B.dis;
    }
    struct road
    {
        int to,w;
        road(int A,int B)
        {
            to=A,w=B;
        }
        friend bool operator < (road A,road B)
        {
            if(A.w==B.w)
                return dis[A.to].zero > dis[B.to].zero;
            return A.w > B.w;
        }
    };
    vector <road> e[N],rev[N];
    int n,m,K,poi,T,rd[N],dl2[N],front2,tail2,dis2[N];
    long long f[N][M];
    priority_queue <road,vector<road> > dl;
    bool vis[N];
    void dj()
    {
        for(int i=1;i<=n;i++)
            dis[i].dis=inf,dis[i].no=i;
        dis[1].dis=0;
        memset(dis2,0x3f,sizeof dis2);
        dis2[1]=0;
        while(dl.empty()==false) dl.pop();
        memset(vis,0,sizeof vis);
        dl.push(road(1,0));
        f[1][0]=1;
        int cnt=0;
        while(cnt!=n and dl.empty()==false)
        {
            road temp=dl.top();
            dl.pop();
            if(vis[temp.to]==true) continue;
            vis[temp.to]=true;
            int now=temp.to,dis_now=temp.w;
            for(int i=0;i<int(e[now].size());i++)
                if(dis_now+e[now][i].w < dis[e[now][i].to].dis)
                {
                    f[e[now][i].to][0]=f[now][0];
                    dis[e[now][i].to].dis=dis_now+e[now][i].w;
                    dis2[e[now][i].to]=dis_now+e[now][i].w;
                    dl.push(road(e[now][i].to,dis[e[now][i].to].dis));
                }
                else if(dis_now+e[now][i].w == dis[e[now][i].to].dis)
                    f[e[now][i].to][0]=(f[e[now][i].to][0]+f[now][0])%poi;
        }
    }
    void GetTP()
    {
        tail2=front2=0;
        memset(vis,0,sizeof vis);
        for(int i=1;i<=n;i++)
            if(rd[i]==0)
                dl2[tail2++]=i;
        int cnt=0;
        while(tail2>front2)
        {
            dis[dl2[front2]].zero=++cnt;
            vis[dl2[front2]]=true;
            for(int i=0;i<int(e[dl2[front2]].size());i++)
                if(e[dl2[front2]][i].w==0 and vis[e[dl2[front2]][i].to]==false)
                {
                    rd[e[dl2[front2]][i].to]--;
                    if(rd[e[dl2[front2]][i].to]==0)
                        dl2[tail2++]=e[dl2[front2]][i].to;
                }
            front2++;
        }
    }
    int main()
    {
        T=read();
        for(int i=1;i<N;i++)
            e[i].reserve(4),rev[i].reserve(4);
        for(;T>0;T--)
        {
            memset(f,0,sizeof f);
            memset(rd,0,sizeof rd);
            n=read(),m=read(),K=read(),poi=read();
            for(int i=1;i<=n;i++)
                e[i].clear(),rev[i].clear();
            for(int i=1;i<=m;i++)
            {
                int a=read(),b=read(),c=read();
                e[a].push_back(road(b,c));
                rev[b].push_back(road(a,c));
                if(c==0)
                    rd[b]++;
            }
            
            GetTP();
            bool OK=true;
            for(int i=1;i<=n;i++)
                if(vis[i]==false)
                    OK=false;
            if(OK==false)
            {
                printf("-1
    ");
                continue;
            }
            dj();
            
            sort(dis+1,dis+1+n,cmp);    
            for(int j=1;j<=K;j++)
                for(int i=1;i<=n;i++)
                    for(int k=0;k<int(rev[dis[i].no].size());k++)
                    {
                        int t=dis[i].no,s=rev[t][k].to;
                        if(dis2[t]!=inf and dis2[s]!=inf and dis2[t]+j-dis2[s]-rev[t][k].w>=0 )
                            f[t][j]=(f[t][j]+f[s][dis2[t]+j-dis2[s]-rev[t][k].w])%poi;
                    }
        
            long long ans=0;
            for(int i=0;i<=K;i++)
                ans=(ans+f[n][i])%poi;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    连续3年!SpreadJS 纯前端表格控件荣获“中国优秀软件产品”
    终于有一款组件可以全面超越Apache POI
    List<Object> 多条件去重
    xml文档的解析并通过工具类实现java实体类的映射:XML工具-XmlUtil
    soap get/post请求
    map转java对象
    springboot postman 对象里传时间格式问题
    spring boot的多环境部署
    Hibernate 之 @Query查询
    利用maven命令将外部jar包导进maven仓库
  • 原文地址:https://www.cnblogs.com/GoldenPotato/p/9676610.html
Copyright © 2011-2022 走看看