[Noip2017]逛公园

和最短路有关的dp

dp[i][j]表示到i点走的路程比i点最短路多j的方案数

因为要从小往大更新,所以要用最短路对点排序

对于含0边的点要注意还要根据拓扑序更新

某0环上的点u,若dis1[u](距1最短路)+disn[u](距n最短路)<=dis1[n]+k,输出-1

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,cnt,mod,t,k,fl,tot;
int dgr[100005];
int twd[100005];
int dis1[100005];
int disn[100005];
int head[100005];
bool vis[100005];
bool zer[100005];
int dp[100005][55];
struct Edge{
    int fr;
    int to;
    int val;
    int nxt;
}edge[200005];
struct Path{
    int fr;
    int to;
    int val;
    int nxt;
}path[200005];
struct node{
    int u;
    int w;
    node(int u,int w):u(u),w(w){}
};
struct par{
    int u;
    int ord;
    int w1;
    int wn; 
}prk[100005];
bool operator<(node a,node b){
    return a.w>b.w;
}
int cmp(par a,par b){
    if(a.w1==b.w1)return a.ord<b.ord;
    return a.w1<b.w1;
}
void init(){
    cnt=fl=tot=0;
    memset(vis,false,sizeof(vis));
    memset(twd,-1,sizeof(twd));
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w){
    cnt++;
    edge[cnt].fr=u;
    edge[cnt].to=v;
    edge[cnt].val=w;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}
void addpath(int u,int v,int w){
    tot++;
    path[tot].fr=u;
    path[tot].to=v;
    path[tot].val=w;
    path[tot].nxt=twd[u];
    twd[u]=tot;
}
void dijk(){
    prk[1].w1=0;
    priority_queue<node>que;
    que.push(node(1,0));
    while(!que.empty()){
        node s=que.top();
        que.pop();
        if(vis[s.u])continue;
        vis[s.u]=true;
        for(int i=head[s.u];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(prk[s.u].w1+edge[i].val<prk[v].w1){
                prk[v].w1=prk[s.u].w1+edge[i].val;
                que.push(node(v,prk[v].w1));
            }
        }
    }
    memset(vis,0,sizeof(vis));
    prk[n].wn=0;
    que.push(node(n,0));
    while(!que.empty()){
        node s=que.top();
        que.pop();
        if(vis[s.u])continue;
        vis[s.u]=true;
        for(int i=twd[s.u];i!=-1;i=path[i].nxt){
            int v=path[i].to;
            if(prk[s.u].wn+path[i].val<prk[v].wn){
                prk[v].wn=prk[s.u].wn+path[i].val;
                que.push(node(v,prk[v].wn));
            }
        }
    }
}
void Topu(){
    memset(vis,false,sizeof(vis));
    queue<int>tpu;
    for(int i=1;i<=cnt;i++){
        if(edge[i].val==0){
            dgr[edge[i].to]++;
        }
    }
    for(int i=1;i<=n;i++){
        if(!dgr[i])tpu.push(i);
    }
    while(!tpu.empty()){
        int s=tpu.front();
        tpu.pop();vis[s]=true;
        for(int i=head[s];i!=-1;i=edge[i].nxt){
            int v=edge[i].to;
            if(edge[i].val==0){
                prk[v].ord=prk[s].ord+1;
                dgr[v]--;
                if(!dgr[v])tpu.push(v);
            }
        }
    }
    fl=0;
    for(int i=1;i<=n;i++){
        if(dgr[i])dgr[i]=0;
        if(!vis[i]){
            if(dis1[i]+disn[i]>dis1[n]+k)continue;
            for(int j=head[i];j!=-1;j=edge[j].nxt){
                int v=edge[i].to;
                if(!vis[v]){
                    fl=1;
                }
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d%d%d%d",&n,&m,&k,&mod);
        for(int i=1;i<=n;i++){
            prk[i].u=i;
            prk[i].ord=0;
            prk[i].w1=0x3f3f3f3f;
            prk[i].wn=0x3f3f3f3f;
            for(int j=0;j<=k;j++){
                dp[i][j]=0;
            }
        }
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addpath(v,u,w);
        }
        dijk();
        for(int i=1;i<=n;i++){
            dis1[i]=prk[i].w1;
            disn[i]=prk[i].wn;
        }
        Topu();
        if(fl){
            printf("-1
");
            continue;
        }
        sort(prk+1,prk+n+1,cmp);
        dp[1][0]=1;
        for(int j=0;j<=k;j++){
            for(int i=1;i<=n;i++){
                for(int f=head[prk[i].u];f!=-1;f=edge[f].nxt){
                    int v=edge[f].to;
                    if(dis1[prk[i].u]+edge[f].val+j-dis1[v]<=k){
                        (dp[v][dis1[prk[i].u]+edge[f].val+j-dis1[v]]+=dp[prk[i].u][j])%=mod;
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<=k;i++){
            (ans+=dp[n][i])%=mod;
        }
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lnxcj/p/9813777.html