zoukankan      html  css  js  c++  java
  • noip2017 逛公园

    https://www.luogu.com.cn/problem/P3953

    k=0:

    直接在spfa过程中最短路计数。

    没有0边:

    定义路径长度的增量为它比最短路多的距离

    dp[i][j]表示从1到i,路径长度增量为j的路径条数

    枚举一条从u->v,距离为w的边

    新的增量为dis(1,u)+w-dis(1,v)+j

    即dp[v][ dis(1,u)+w-dis(1,v)+j ]+=dp[u][j]

    如果新的增量不等于原来的增量,那么从0到k枚举增量,转移不用考虑顺序

    但是如果走的边不会产生新的增量,点与点之间的转移存在先后顺序,可以将所有点按1号点到它的最短路从小到大排序

    有0边:

    先不考虑无穷的情况

    0边带来的影响是,按最短路排序无法明确0边起点终点的先后顺序

    因为0边的起点肯定要在终点的前面,才能让0边起点的贡献通过0边终点转移到其他点

    既然如此可以拓扑排序

    那什么时候有无穷多组解呢?

    直观的情况是有一个不会产生新增量的环,在环上无限转圈

    那么将会经过的且不会产生新增量的边构建新图,判断新图上有没有环即可

    即将满足以下2个要求的边(从u到v,长度为w)构建新的图:

    1、dis(1,u)+w+dis(v,n)<=dis(1,n)+k (该边会经过)

    2、dis(1,u)+w=dis(1,v)  (从1走过来,该边不会产生新的增量)

    判环也是拓扑排序即可

    在新图上dp的过程中

    先进行不会产生新增量的转移,即dp[u][j]向dp[v][j]的转移,转移顺序按拓扑序

    再进行会产生新增量的转移,即dp[u][j]向dp[v][ dis(1,u)+w-dis(1,v)+j ],因为产生了新的增量,所以顺序随便

    #include<queue>
    #include<cstdio>
    #include<cstring>
    
    using namespace std; 
    
    #define  N 100001
    
    int T,n,m,k,p;
    int eu[N<<1],ev[N<<1],ew[N<<1];
    int front[N],to[N<<2],nxt[N<<2],val[N<<2],tot,front2[N];
    
    queue<int>q;
    int dis[N],dis2[N];
    bool vis[N];
    
    int in[N]; 
    int st[N],id[N];
    
    int dp[N][51];
    
    int nn;
    bool use[N];
    
    void add(int u,int v,int w,int *head)
    {
        to[++tot]=v; nxt[tot]=head[u]; head[u]=tot; val[tot]=w;
    }
    
    void spfa(int *head,int start,int *d)
    {
        for(int i=1;i<=n;++i) d[i]=1e9;
        d[start]=0;
        q.push(start);
        vis[start]=true;
        int now,t; 
        while(!q.empty())
        {
            now=q.front();
            q.pop();
            vis[now]=false;
            for(int i=head[now];i;i=nxt[i])
            {
                t=to[i];
                if(d[t]>d[now]+val[i])
                {
                    d[t]=d[now]+val[i];
                    if(!vis[t])
                    {
                        q.push(t);
                        vis[t]=true;
                    }
                }
            }
        }
    }
    
    void rebuild()
    {
        memset(front,0,sizeof(front));
        memset(in,0,sizeof(in));
        memset(use,false,sizeof(use));
        tot=0;
        for(int i=1;i<=m;++i)
        {
            if(dis[eu[i]]+dis2[ev[i]]+ew[i]<=dis[n]+k && dis[eu[i]]+ew[i]==dis[ev[i]])
            {
                add(eu[i],ev[i],0,front);
                in[ev[i]]++;
                use[eu[i]]=true;
                use[ev[i]]=true;
            }
        }
        nn=0;
        for(int i=1;i<=n;++i) 
            if(use[i]) ++nn;
        for(int i=1;i<=m;++i) ew[i]=dis[eu[i]]+ew[i]-dis[ev[i]];
    }
    
    bool topsort()
    {
        int now,top,dn=0;
        st[top=1]=1;
        id[dn=1]=1;
        while(top)
        {
            now=st[top--];
            for(int i=front[now];i;i=nxt[i])
            {
                in[to[i]]--;
                if(!in[to[i]])
                {
                    id[++dn]=to[i];
                    st[++top]=to[i];
                }
            }
        }
        for(int i=1;i<=n;++i)
            if(in[i]) return false;
        return true;
    }
    
    void solve()
    {
        memset(dp,0,sizeof(dp));
        dp[1][0]=1;
        int now,ans=0;
        for(int i=0;i<=k;++i)
        {
            for(int j=1;j<=nn;++j)
            {
                now=id[j];
                for(int l=front[now];l;l=nxt[l]) 
                    dp[to[l]][i]=(dp[to[l]][i]+dp[now][i])%p;
            }
            for(int j=1;j<=m;++j)
            {
                if(i+ew[j]<=k && ew[j]>0) 
                    dp[ev[j]][i+ew[j]]=(dp[ev[j]][i+ew[j]]+dp[eu[j]][i])%p;
            }
            ans=(ans+dp[n][i])%p;
        }
        printf("%d
    ",ans);
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d%d",&n,&m,&k,&p);
            tot=0;
            memset(front,0,sizeof(front));    
            for(int i=1;i<=m;++i)
            {
                scanf("%d%d%d",&eu[i],&ev[i],&ew[i]);
                add(eu[i],ev[i],ew[i],front);
            }
            spfa(front,1,dis);
            memset(front2,0,sizeof(front2));
            for(int i=1;i<=m;++i)
            {
                add(ev[i],eu[i],ew[i],front2);
            }
            spfa(front2,n,dis2);
            rebuild();
            if(!topsort())
            {
                printf("-1
    ");
                continue;
            }
            solve();
        }
        return 0;
    }
    
    /*
    1
    6 7 3 100
    1 2 0
    2 3 0
    3 4 0
    4 5 1
    5 2 0
    2 5 0
    5 6 0
    */
  • 相关阅读:
    解决Android的ListView控件滚动时背景变黑
    倒计时实现网页跳转
    java获取当前路径
    JavaScript笔记之面向对象
    Jqery调用WebService(.Net3.5环境下)
    Android开发:TabActivity中onKeyDown无法响应的解决方法
    最近不知道什么时候开始D3D11CreateDevice返回E_FAIL
    C++ allocator及一些影响程序性能的因素
    AOP切面编程 几年前写的,移过来
    Adaptive Transparency
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/13953290.html
Copyright © 2011-2022 走看看