LG3953 逛公园

题意

策策同学特别喜欢逛公园。公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,\(N\)号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从\(1\)号点进去,从\(N\)号点出来。

策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果\(1\)号点 到\(N\)号点的最短路长为\(d\),那么策策只会喜欢长度不超过\(d+K\)的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

为避免输出过大,答案对\(P\)取模。

如果有无穷多条合法的路线,请输出\(-1\)

\(N \leq 100000,M \leq 200000,K \leq 50\)

分析

考虑dp。用\(f(x,lim)\)表示从x到n的路径长不超过dis(x,n)+lim的路径条数。

求出最短路后,就可以dfs求解,为了处理环的情况传进两个参数x和lim。

反向建图跑最短路可以优秀卡常,并且若跑完最短路后距离仍为INF就可以剪枝。

然后是无解的情况,如果有0环则有无数解。方法是SPFA或者dfs记录状态,若从某状态再次搜到相同的状态则肯定有0环。SPFA可能被卡,所以采用Dijkstra+dfs判0环的方法。

时间复杂度\(O(K M)\)

代码

dfs

中规中矩的记忆化搜索,是绝对的首选。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<cassert>
#include<stack>
#include<vector>
template<class T>T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return x=data*w;
}
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;

const int MAXN=1e5+7,MAXM=2e5+7,MAXK=60;
int n,m,k,mod;

vector <pii> g1[MAXN],g2[MAXN];

void init()
{
    read(n);read(m);read(k);read(mod);
    for(int i=1;i<=n;++i)
    {
        g1[i].clear();
        g2[i].clear();
    }
    for(int i=1;i<=m;++i)
    {
        int x,y,w;
        read(x);read(y);read(w);
        g1[x].push_back(pii(y,w));
        g2[y].push_back(pii(x,w));
    }
}

int dis[MAXN];

void Dijkstra(int s)
{
    fill(dis+1,dis+n+1,INF);
    dis[s]=0;
    priority_queue <pii> H;
    H.push(pii(-dis[s],s));
    while(H.size())
    {
        int x=H.top().second,d=-H.top().first;
        H.pop();
        if(d>dis[x])
            continue;
        for(int i=0;i<g2[x].size();++i)
        {
            int y=g2[x][i].first,w=g2[x][i].second;
            if(dis[y]>dis[x]+w)
            {
                dis[y]=dis[x]+w;
                H.push(pii(-dis[y],y));
            }
        }
    }
}

int add(int x,int y)
{
    x+=y;
    return x>=mod?x-mod:x;
}

bool vis[MAXN][MAXK];
bool ins[MAXN][MAXK];
int f[MAXN][MAXK];

int dfs(int x,int lim)
{
//  cerr<<"dfs "<<x<<" "<<lim<<endl;
    if(vis[x][lim])
        return f[x][lim];
    if(ins[x][lim])
        return -1;
    ins[x][lim]=1;
    int&ans=f[x][lim]=(x==n);
    for(int i=0;i<g1[x].size();++i)
    {
        int y=g1[x][i].first,w=g1[x][i].second;
        if(dis[y]==INF)
            continue;
        int delta=dis[y]+w-dis[x];
        if(delta>lim)
            continue;
        int t=dfs(y,lim-delta);
        if(t==-1)
            return -1;
        ans=add(ans,t);
    }
    vis[x][lim]=1;
    ins[x][lim]=0;
    return ans;
}

int main()
{
    freopen("park.in","r",stdin);
    freopen("park.out","w",stdout);
//  freopen("park.err","w",stderr);
    int T;
    read(T);
    while(T--)
    {
        init();
        Dijkstra(n);
//      cerr<<"dis="<<endl;
//      for(int i=1;i<=n;++i)
//          cerr<<i<<" d="<<dis[i]<<endl;
        for(int i=1;i<=n;++i)
        {
            fill(vis[i],vis[i]+k+1,0);
            fill(ins[i],ins[i]+k+1,0);
        }
        printf("%d\n",dfs(1,k));
    }
    return 0;
}

SPFA

要是用SPFA就要有信仰了,不可能入队n次才判0环,不然肯定TLE。

于是我逐渐去试,发现如果设成10会WA,设成11刚好能AC。出题人根本就没造网格图,但考试的时候谁又知道呢?

然后就跑得飞快。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
template<class T>T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return x=data*w;
}
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;

const int MAXN=1e5+7,MAXM=2e5+7,MAXK=60;
int n,m,k,mod;

struct edge // g2
{
    int nx,to,w;
}e[MAXM];
int head[MAXN],ecnt;
vector <pii> g[MAXN]; // g1

void addedge(int x,int y,int w)
{
    e[++ecnt].to=y,e[ecnt].w=w;
    e[ecnt].nx=head[x],head[x]=ecnt;
}

void init()
{
    read(n);read(m);read(k);read(mod);
    ecnt=0;
    fill(head+1,head+n+1,0);
    for(int i=1;i<=n;++i)
        g[i].clear();
    for(int i=1;i<=m;++i)
        {
            int x,y,w;
            read(x);read(y);read(w);
            addedge(y,x,w);
            g[x].push_back(pii(y,w));
        }
}

bool inq[MAXN];
int cnt[MAXN];
int dis[MAXN];

bool SPFA(int s)
{
    fill(inq+1,inq+n+1,0);
    fill(cnt+1,cnt+n+1,0);
    fill(dis+1,dis+n+1,INF);
    dis[s]=0;
    queue<int>Q;
    Q.push(s);
    inq[s]=1;
    ++cnt[s];
    while(Q.size())
    {
        int x=Q.front();
        Q.pop();
        inq[x]=0;
        for(int i=head[x];i;i=e[i].nx)
        {
            int y=e[i].to,w=e[i].w;
            if(dis[y]>=dis[x]+w)
            {
                dis[y]=dis[x]+w;
                if(!inq[y])
                {
                    Q.push(y);
                    inq[y]=1;
                    if(++cnt[y]>=11) // 0 circle
                        return 0;
                }
            }
        }
    }
    return 1;
}

int add(int x,int y)
{
    x+=y;
    return x>=mod?x-mod:x;
}

bool vis[MAXN][MAXK];
int f[MAXN][MAXK];

int dfs(int x,int lim)
{
//  cerr<<"dfsing "<<x<<" lim="<<lim<<endl;
    if(lim<0)
        return 0;
    if(vis[x][lim])
        return f[x][lim];
    int&ans=f[x][lim]=(x==n);
    for(int i=0;i<g[x].size();++i)
    {
        int y=g[x][i].first,w=g[x][i].second;
        if(!cnt[y]) // not accessible to n
            continue;
        int delta=dis[y]+w-dis[x];
        ans=add(ans,dfs(y,lim-delta));
    }
    vis[x][lim]=1;
//  cerr<<" ans="<<ans<<endl;
    return ans;
}

int main()
{
    freopen("park.in","r",stdin);
    freopen("park.out","w",stdout);
//  freopen("park.err","w",stderr);
    int T;
    read(T);
    while(T--)
    {
        init();
        if(!SPFA(n)) // 0 circle
        {
            puts("-1");
            continue;
        }
//      cerr<<"dis="<<endl;
//      for(int i=1;i<=n;++i)
//          cerr<<i<<" d="<<dis[i]<<endl;
        for(int i=1;i<=n;++i)
            fill(vis[i],vis[i]+k+1,0);
        printf("%d\n",dfs(1,k));
    }
    return 0;
}

Hint

注意数组范围。

猜你喜欢

转载自www.cnblogs.com/autoint/p/9931611.html
今日推荐