[loj2316][NOIP2017]逛公园——拓扑排序+dp 大佬们的博客 Some Links

题目大意:

给定一个有向图,求从1到n的路径长度不大于mindis[n]+K的路径条数。

思路:

很容易想到DP,设dp[i][j]为到第i个点长度比最短路大j的路径的条数,每一个点向后面的点转移即可。
发现一个性质,就是转移的时候j必定是单调不递减的,也就是如果某一条转移不是沿着最短路走的话就必定不会在转移图中出现环。如果一条环上都可以是沿着最短路走的话就会出现环,不难发现这种情况就是0权环,因为如果不是0权环的话就必定不是最短路,于是我们可以对动态转移图进行拓扑排序之后动态规划,最后判断一下有没有环就好了。
以上的做法纯属自己瞎想的,虽然复杂度看起来很对,但是常数很大,实质是暴力处理出转移顺序。
一般的做法是枚举j然后每一个j单独做一次转移。如果不是沿着最短路走的话就没有必要注意在第j层的顺序,但是如果是沿着最短路走的话就要对最短路生成图进行拓扑排序,然后每一层单独在拓扑序上面转移。
最后靠着卡常以及O2卡过去的代码。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;

using namespace std;

void File(){
    freopen("loj2316.in","r",stdin);
    freopen("loj2316.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=1e5+10;
const int maxm=2e5+10;
const int maxk=50+10;
int T,n,m,k,deg[maxn][maxk];
int beg[maxn],to[maxm],las[maxm],w[maxm],cnte=1;
int mod,dis[maxn],dp[maxn][maxk];
bool vis[maxn];
queue<int>qu;
pii du[maxn*maxk];
int head,tail;
struct node{
    int u,v,w;
    bool operator < (const node & tt) const {
        return u<tt.u;
    }
}E[maxm];

void add(int u,int v,int ww){las[++cnte]=beg[u];beg[u]=cnte;to[cnte]=v;w[cnte]=ww;}

void spfa(){
    memset(dis,63,sizeof(dis));
    dis[1]=0; qu.push(1);
    while(qu.size()){
        int u=qu.front();
        qu.pop(); vis[u]=0;
        for(int i=beg[u];i;i=las[i]){
            if(dis[u]+w[i]<dis[to[i]]){
                dis[to[i]]=dis[u]+w[i];
                if(!vis[to[i]]){
                    vis[to[i]]=1;
                    qu.push(to[i]);
                }
            }
        }
    }
}

void init(){
    read(n); read(m); read(k); read(mod);
    int u,v,ww;
    REP(i,1,m)read(u),read(v),read(ww),E[i]=(node){u,v,ww};
    sort(E+1,E+m+1);
    REP(i,1,m)add(E[i].u,E[i].v,E[i].w);
    spfa();
    REP(i,1,n)for(int j=beg[i];j;j=las[j])REP(f,0,k)
        if(f+dis[i]+w[j]-dis[to[j]]<=k)
            ++deg[to[j]][f+dis[i]+w[j]-dis[to[j]]];
}

void update(int &_,int __){_=_+__; if(_>mod)_-=mod;}

bool work(){
    int cnt=0;
    dp[1][0]=1;
    head=1; tail=0;
    REP(i,1,n)REP(j,0,k)if(!deg[i][j])
        du[++tail]=(mk(i,j)),++cnt;
    int tot=0,tot1=0;
    while(head<=tail){
        int x=du[head].fi,y=du[head].se; ++head;
        for(int i=beg[x];i;i=las[i]){
            ++tot;
            int val=y+dis[x]+w[i]-dis[to[i]];
            if(val<=k){
                ++tot1;
                update(dp[to[i]][val],dp[x][y]);
                --deg[to[i]][val];
                if(!deg[to[i]][val])du[++tail]=mk(to[i],val),++cnt;
            }
        }
    }
    return cnt==n*(k+1);
}

int main(){
    File();
    read(T);
    while(T--){
        cnte=1;
        memset(beg,0,sizeof(beg));
        memset(deg,0,sizeof(deg));
        memset(dp,0,sizeof(dp));
        init();
        if(!work())puts("-1");
        else{
            int ans=0;
            REP(j,0,k)update(ans,dp[n][j]);
            printf("%d\n",ans);
        }
    }
    //cout<<(double)clock()/CLOCKS_PER_SEC<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81809623