NOIP2017 Day1 T3 逛公园(最短路+拓扑排序+DP)

Posted Sakits

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017 Day1 T3 逛公园(最短路+拓扑排序+DP)相关的知识,希望对你有一定的参考价值。

  神tm比赛时多清个零就有60了T T

  首先跑出1起点和n起点的最短路,因为k只有50,所以可以DP。设f[i][j]表示比最短路多走i的长度,到j的方案数。

  我们发现如果在最短路上的和零边会有后向性,怎么办呢?拓扑排序。

  把最短路上的点和零边的点拉出来跑拓扑排序,如果有零环的话必定度数不为0,而且要注意零环必须在<=最短路+k的路径上才输出-1,这个就用刚刚跑出来的1起点到n起点的最短路来判断就好了。

  然后先按拓扑序DP出i相同的,然后再DP不在最短路上或者零边的。

技术分享图片
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
using namespace std;
const int maxn=1000010;
struct tjm{int too, dis, pre;}e[2][maxn];
struct poi{int x, dis;};
priority_queue<poi>q;
bool operator<(poi a, poi b){return a.dis>b.dis;}
int T, x, y, z, n, m, K, mod, top, tot[2], ans;
int f[60][maxn], dist[2][maxn], last[2][maxn], d[maxn], st[maxn];
void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<0 || c>9) c==- && (f=-1), c=getchar();
    while(c<=9 && c>=0) k=k*10+c-0, c=getchar();
    k*=f;
}
inline void add(int x, int y, int z, int ty){e[ty][++tot[ty]]=(tjm){y, z, last[ty][x]}; last[ty][x]=tot[ty];}
inline void dij(int x, int ty)
{
    memset(dist[ty], 32, sizeof(dist[ty]));
    dist[ty][x]=0; q.push((poi){x, 0});
    while(!q.empty())
    {
        poi now=q.top(); q.pop();
        if(dist[ty][now.x]!=now.dis) continue;
        for(int i=last[ty][now.x], too;i;i=e[ty][i].pre)
        if(dist[ty][too=e[ty][i].too]>dist[ty][now.x]+e[ty][i].dis)
        {
            dist[ty][too]=dist[ty][now.x]+e[ty][i].dis;
            q.push((poi){too, dist[ty][too]});
        }
    }
}
inline bool topo()
{
    memset(d, 0, sizeof(d));
    for(int i=1;i<=n;i++)
    for(int j=last[0][i], too;j;j=e[0][j].pre)
    if(dist[0][i]+e[0][j].dis==dist[0][too=e[0][j].too]) d[too]++;
    top=0; for(int i=1;i<=n;i++) if(!d[i]) st[++top]=i;
    for(int i=1;i<=top;i++)
    for(int j=last[0][st[i]], too;j;j=e[0][j].pre)
    if(dist[0][st[i]]+e[0][j].dis==dist[0][too=e[0][j].too])
    {
        d[too]--;
        if(!d[too]) st[++top]=too;
    }
    for(int i=1;i<=n;i++) if(d[i] && dist[0][i]+dist[i][n]<=dist[0][n]+K) return 0;
    return 1;
}
int main()
{
    read(T);
    while(T--)
    {
        memset(last, 0, sizeof(last)); tot[0]=tot[1]=0; 
        read(n); read(m); read(K); read(mod);
        for(int i=1;i<=m;i++) read(x), read(y), read(z), add(x, y, z, 0), add(y, x, z, 1);
        dij(1, 0); dij(n, 1);
        if(!topo()) {puts("-1"); continue;}
        memset(f, 0, sizeof(f)); f[0][1]=1; ans=0;
        for(int i=0;i<=K;i++)
        {
            for(int j=1;j<=top;j++) 
            for(int k=last[0][st[j]], too;k;k=e[0][k].pre)
            if(e[0][k].dis+dist[0][st[j]]==dist[0][too=e[0][k].too])
            f[i][too]+=f[i][st[j]], f[i][too]=MOD(f[i][too]);
            for(int j=1;j<=n;j++)
            for(int k=last[0][j], too, tmp;k;k=e[0][k].pre)
            if((tmp=i+e[0][k].dis+dist[0][j]-dist[0][too=e[0][k].too])<=K && i!=tmp)
            f[tmp][too]+=f[i][j], f[tmp][too]=MOD(f[tmp][too]);
            ans+=f[i][n]; ans=MOD(ans);
        }
        printf("%d\n", ans);
    }
}
View Code

 

 

以上是关于NOIP2017 Day1 T3 逛公园(最短路+拓扑排序+DP)的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划

洛谷 P3953 [NOIP2017 提高组] 逛公园(最短路,记忆化搜索)

luogu 3953 逛公园

[luogu P3953] [noip2017 d1t3] 逛公园

比赛NOIP2017 逛公园

P3953 NOIP2017 d1t3 逛公园