• 洛谷P3953 逛公园


    传送门

    设dp[i][j]为从1到i长度为最短路+j的路径数量,dis1为从1到每个点的最短距离,disn为从n到每个点的最短距离。先不考虑0边,dp[u][j]可以转移到v的dis[u]+w(u,v)-dis[v]+j,即dp[u][j]->dp[v][dis[u]+w(u,v)-dis[v]+j]。dis[u]+w(u,v)-dis[v]即松弛距离。在有向图上,可能前面的点会由后面的点松弛到,因此dp转移的时候外层枚举0->k,内层枚举按dis1排序的节点,分层转移。

    接下来考虑0边的影响。0边可能会产生0环,如果0环恰好在有效路径内,则会出现无穷条路径满足情况。如果0环上的点满足dis1[i]+disn[i]<=dis1[n]+k,那么这个0环跑无穷多次都是满足题意的,判断这种情况输出-1。于是要找出在0环上的点,把所有0边取出来建图进行拓扑排序,如果排序完有入度不为0的点则存在0环。但跑一次拓扑只是把所有指向0环的边摘掉了,可能还有0环指向的边存在于图中。因此建0边的反图,再跑一次拓扑排序,把所有原图中0环指向的边也摘掉,剩下的既被0边连接又没有被两次拓扑摘掉的点就是0环上的点。

    存在0边以后,如果dp时仍然只按dis1排序,在0边相连的点之间可能会存在顺序问题。例如,存在x->y->z,中间相连的两条边都为0,那么转移的时候应该由x到y再到z,实际上因为三个点的dis1相同,可能不会按正确顺序排序。那么在第一次拓扑排序的时候顺便记录与0边相连的点的拓扑序,dp时排序以dis1为第一关键字,拓扑序为第二关键字即可。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int inf=214748364;
    int a[100010],now;
    int t,n,m,k,p,ru[100010],tag[100010],flag,zero[100010],zer,vis1[100010],vis2[100010];
    long long ans;
    int dis[100010],vis[100010];
    bool cmp(int x,int y){
        if(dis[x]==dis[y])return tag[x]<tag[y];
        else return dis[x]<dis[y];
    }
    struct edg{
        int x,y;
        edg(int a=0,int b=0){
            x=a,y=b;
        }
    }ze[200010];
    int ver[200010],Next[200010],head[100010],tot,edge[200010];
    void add(int x,int y,int z){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
        edge[tot]=z;
    }
    int ver1[200010],Next1[200010],head1[100010],tot1,edge1[200010];
    void add1(int x,int y,int z){
        ver1[++tot1]=y;
        Next1[tot1]=head1[x];
        head1[x]=tot1;
        edge1[tot1]=z;
    }
    priority_queue<pair<int,int> >q;
    void dij(){
        while(q.size())q.pop();
        for(int i=1;i<=n;i++)dis[i]=inf,vis[i]=0;
        dis[1]=0;
        q.push(make_pair(0,1));
        while(!q.empty()){
            while(q.size()&&vis[q.top().second])q.pop();
            if(q.empty())break;
            int x=q.top().second;
            q.pop();
            vis[x]=1;
            for(int i=head[x];i;i=Next[i]){
                int y=ver[i];
                if(dis[y]>dis[x]+edge[i]){
                    dis[y]=dis[x]+edge[i];
                    q.push(make_pair(-dis[y],y));
                }
            }
        }
    }
    int dis1[100010];
    void dij1(){
        while(q.size())q.pop();
        for(int i=1;i<=n;i++)dis1[i]=inf,vis[i]=0;
        dis1[n]=0;
        q.push(make_pair(0,n));
        while(!q.empty()){
            while(q.size()&&vis[q.top().second])q.pop();
            if(q.empty())break;
            int x=q.top().second;
            q.pop();
            vis[x]=1;
            for(int i=head1[x];i;i=Next1[i]){
                int y=ver1[i];
                if(dis1[y]>dis1[x]+edge1[i]){
                    dis1[y]=dis1[x]+edge1[i];
                    q.push(make_pair(-dis1[y],y));
                }
            }
        }
    }
    queue<int>qq;
    void tp1(){
        while(qq.size())qq.pop();
        for(int i=1;i<=n;i++){
            if(!ru[i]&&zero[i])qq.push(i);
        }
        while(qq.size()){
            int x=qq.front();
            qq.pop();
            tag[x]=++now;
            vis1[x]=1;
            for(int i=head1[x];i;i=Next1[i]){
                int y=ver1[i];
                ru[y]--;
                if(!ru[y]){
                    qq.push(y);
                }
            }
        }
    }
    void tp2(){
        while(qq.size())qq.pop();
        for(int i=1;i<=n;i++){
            if(!ru[i]&&zero[i])qq.push(i);
        }
        while(qq.size()){
            int x=qq.front();
            qq.pop();
            vis2[x]=1;
            for(int i=head1[x];i;i=Next1[i]){
                int y=ver1[i];
                ru[y]--;
                if(!ru[y]){
                    qq.push(y);
                }
            }
        }
        for(int i=1;i<=n;i++){
            if(zero[i]&&!vis1[i]&&!vis2[i]){
                if(dis[i]+dis1[i]<=dis[n]+k)flag=1;
            }
        }
    }
    long long dp[100010][51];
    void work(){
        dp[1][0]=1%p;
        for(int i=0;i<=k;i++){
            for(int j=1;j<=n;j++){
                int x=a[j];
                if(!dp[x][i])continue;
                for(int l=head[x];l;l=Next[l]){
                    int y=ver[l];
                    if(i+dis[x]+edge[l]-dis[y]<=k){
                        dp[y][i+dis[x]+edge[l]-dis[y]]=(dp[y][i+dis[x]+edge[l]-dis[y]]+dp[x][i])%p;
                    }
                }
            }
        }
    }
    void clear(){
        now=zer=ans=tot=tot1=flag=0;
        memset(dp,0,sizeof(dp));
        memset(ru,0,sizeof(ru));
        memset(head,0,sizeof(head));
        memset(head1,0,sizeof(head1));
        memset(tag,0,sizeof(tag));
        memset(vis1,0,sizeof(vis1));
        memset(vis2,0,sizeof(vis2));
        memset(zero,0,sizeof(zero));
    }
    int main(){
    //    freopen("1.in","r",stdin);
        scanf("%d",&t);
        while(t--){
            scanf("%d%d%d%d",&n,&m,&k,&p);
            clear();
            for(int i=1,x,y,z;i<=m;i++){
                scanf("%d%d%d",&x,&y,&z);
                if(!z){
                    ze[++zer]=edg(x,y);
                    zero[x]=zero[y]=1;
                }
                add(x,y,z);
                add1(y,x,z);
            }
            dij();
            dij1();
            memset(head1,0,sizeof(head1));
            tot1=0;
            for(int i=1;i<=zer;i++){
                add1(ze[i].x,ze[i].y,0);
                ru[ze[i].y]++;
            }
            tp1();
            memset(head1,0,sizeof(head1));
            memset(ru,0,sizeof(ru));
            tot1=0;
            for(int i=1;i<=zer;i++){
                add1(ze[i].y,ze[i].x,0);
                ru[ze[i].x]++;
            }
            tp2();
            if(flag){
                printf("-1
    ");
                continue;
            }
            for(int i=1;i<=n;i++){
                a[i]=i;
            }
            sort(a+1,a+n+1,cmp);
            work();
            for(int i=0;i<=k;i++){
                ans=(ans+dp[n][i])%p;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【训练】9.13 训练赛
    【训练】9.12 训练赛
    【题录】CF#666 Div.2
    【题解】JSOI2009球队收益 / 球队预算
    【申明】——暂别博客园——
    【题解】CF#896 D-Nephren Runs a Cinema
    【题解】洛谷P4707重返现世
    [HNOI2012][BZOJ2732] 射箭 [二分+半平面交]
    平面几何-学习笔记
    [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]
  • 原文地址:https://www.cnblogs.com/chloris/p/11853576.html
Copyright © 2020-2023  润新知