P3953 逛公园

传送门

花了一个下午才 A 的毒瘤题

思路:

  这题需要建两个图,一个正向图,一个反向图。

  先在正向图上跑一遍 dijkstar ,计算出每个点到 点1 的最短路径 。

  然后在反向图上开始记忆化搜索:

  - 和动规一样,先定义 f [ i ][ j ] 表示:从 点 1 到 点 i 的距离为 dis [ i ] + j 的方案数。(初始值要为负,不然判断 0环 的时候会出错)

  - 对于每一条反向边(u,v,w)都有 f [ u ][ d ] = ∑ f [ u ][(dis[ u ] + d)-(dis[ v ] + w)] 。

    设 lck_dis = (dis[ u ] + d)-(dis[ v ] + w)。

    仔细分析会发现 lck_dis 就表示 点 u 到 起点 1 的距离。

    这类似于 最短路计数 的转移,因为建的是反向边,所以要从后往前转移。

  - 为了判断是否有毒瘤的 0边,需要开一个布尔数组 flag [ i ][ j ] 判断 dp 值是否被经过 2 次,且没有被确定,那么一定是有 0边 导致的自环。

Code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<string>
#include<stack>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<set>
using namespace std;
#define INF 0x3f
const int maxn=1e5+7;
inline int read()
{
    int xs=0,kr=1;
    char ls;
    ls=getchar();
    while(!isdigit(ls))
    {
        if(ls=='-')
            kr=-1;
        ls=getchar();
    }
    while(isdigit(ls))
    {
        xs=(xs<<1)+(xs<<3)+(ls^48);
        ls=getchar();
    }
    return xs*kr;
}
int T,n,m,k,p;
long long ans;
int head_dij[maxn],head_dfs[maxn],dis[maxn],f[maxn][61];
bool bo,vis[maxn],flag[maxn][61];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
struct hh
{
    int nex,to,w;
} t_dij[maxn<<1],t_dfs[maxn<<1];
inline void clear()
{
    bo=false;
    memset(head_dij,0,sizeof(head_dij));
    memset(head_dfs,0,sizeof(head_dfs));
    memset(dis,INF,sizeof(dis));
    memset(vis,false,sizeof(vis));
    memset(f,-1,sizeof(f));
}
inline void add(int cnt,int nex,int to,int w)
{
    t_dij[cnt].to=to; t_dij[cnt].w=w;
    t_dij[cnt].nex=head_dij[nex]; head_dij[nex]=cnt;

    t_dfs[cnt].to=nex; t_dfs[cnt].w=w;
    t_dfs[cnt].nex=head_dfs[to]; head_dfs[to]=cnt;
}
inline void dijkstra(int s)
{
    dis[s]=0;
    q.push(make_pair(0,s));
    while (!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u]=true;
        for (int i=head_dij[u];i;i=t_dij[i].nex)
        {
            int v=t_dij[i].to,w=t_dij[i].w;
            if (!vis[v]&&dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                q.push(make_pair(dis[v],v));
            }
        }
    }
}
int dfs(int u,int d)
{
    if (flag[u][d])
    {
        bo=true;
        return 0;
    }
    if (~f[u][d]) return f[u][d];
    flag[u][d]=true;f[u][d]=0;
    for (int i=head_dfs[u];i;i=t_dfs[i].nex)
    {
        if(bo) break;
        int v=t_dfs[i].to,w=t_dfs[i].w;
        int lck_dis=(dis[u]+d)-(w+dis[v]);
        if (lck_dis>=0) f[u][d]=(f[u][d]+dfs(v,lck_dis))%p;
    }
    flag[u][d]=false;
    if (u==1&&!d) return ++f[u][d];
    return f[u][d];    
}
int x,y,z;
int main()
{
    //freopen("park.in","r",stdin);
    //freopen("park.out","w",stdout);
    T=read();
    while (T--)
    {
        clear();
        n=read();m=read();k=read();p=read();
        for(int i=1;i<=m;i++)
        {
            x=read();y=read();z=read();
            add(i,x,y,z);
        }
        dijkstra(1);
        ans=dfs(n,0)%p;
        for(int i=1;i<=k;i++)
        {
            if(bo) break;
            ans=(ans+dfs(n,i))%p;
        }
        if(bo) printf("-1
");
        else printf("%lld
",ans);
    }
return 0;
}

注:在 dfs 中,if ( ~ f [ u ][ d ] ) ⇔ if ( f [ u ][ d ] ≥ 0 ) 。有助于卡常

原文地址:https://www.cnblogs.com/lck-lck/p/9898100.html