【题解】LuoGu3953/noip2017:逛公园

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/83278935


原题传送门

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

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

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

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

为避免输出过大,答案对PP取模。

如果有无穷多条合法的路线,请输出-1−1。

输入输出格式
输入格式:
第一行包含一个整数 TT, 代表数据组数。

接下来TT组数据,对于每组数据: 第一行包含四个整数 N,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。

接下来MM行,每行三个整数a_i,b_i,c_ia
i
​ ,b
i
​ ,c
i
​ ,代表编号为a_i,b_ia
i
​ ,b
i
​ 的点之间有一条权值为 c_ic
i
​ 的有向边,每两个整数之间用一个空格隔开。

输出格式:
输出文件包含 TT 行,每行一个整数代表答案。
输入样例
2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0
输出样例
3
-1
数据范围见链接~~

我是真的不会做==
因为太蒻一开始实在是想不出如何处理-1的情况,先码了70分(没有-1的情况)的

70分思路:

  • 既然题目跟最短路有关,那么先把到1的最短路搞定,堆优化dijkstra套进来,从1到u的最短路用dis[u]表示
  • 想了想,求合法路线的条数,好像有点dp的味道,决定了,就dp吧!
  • 设计:dp[u][l]表示,当前结点为u,从1到u有多少条长度<=dis[u]+l的路径
  • 转移:有一条边u->v,若满足 d i s [ u ] + l + l e n [ u &gt; v ] d i s [ v ] &lt; = k dis[u]+l+len[u-&gt;v]-dis[v]&lt;=k ,则把dp[u][l]累加至dp[v][上面那一坨东西]
  • 最终答案就是dp[n][k]

然后我兴奋地边颓游戏边敲代码弄完了,出了个30分。。上面的思路可能有误,请谨慎食用
搞不懂为什么只有30分,只能去看题解的满分算法,发现题解的70分算法跟我差不多

100分思路:

  • 其实上面的时间复杂度和思路已经OK了,就是-1情况的处理
  • 发现只有出现0环(即一个边全为0的环),就有-1情况出现,因为你可以一直在环上绕来绕去
  • 那么dp处理的话就有些不好了(虽然也可以),用记忆化搜索更方便些
  • 思路还是原来的思路,就是再开个flag[u][l]表示{u,l}是否前面来过,如果重复走,那么就说明有0环了
  • 然后,搜索的时候你是跑反向边的

Code:

var
    edge, edge2 : array[0..4000000] of record
        t, len, next : longint;
    end;
    heap : array[0..1000000] of record
        node, len : longint;
    end;
    dis, head, head2 : array[0..4000000] of longint;
    vis : array[0..1000000] of boolean;
    flag : array[0..1000000,0..50] of boolean;
    f : array[0..1000000,0..50] of longint;
    qnum, q, len, num, num2, n, m, k, p : longint;

procedure add(x, y, z : longint);

begin
    inc(num);
    edge[num].t := y;
    edge[num].len := z;
    edge[num].next := head[x];
    head[x] := num;
end;

procedure add2(x, y, z : longint);

begin
    inc(num2);
    edge2[num].t := y;
    edge2[num].len := z;
    edge2[num].next := head2[x];
    head2[x] := num2;
end;

procedure init;
var
    i, x, y, z : longint;

begin
    readln(n, m, k, p);
    num := 0; num2 := 0;
    fillchar(head, sizeof(head), 0);
    fillchar(edge, sizeof(edge), 0);
    fillchar(head2, sizeof(head2), 0);
    fillchar(edge2, sizeof(edge2), 0);
    for i := 1 to m do
    begin
        readln(x, y, z);
        add(x, y, z);//正向边
        add2(y, x, z);//反向边
    end;
end;

procedure swap(var x, y : longint);
var
    tmp : longint;

begin
    tmp := x; x := y; y := tmp;
end;

procedure push(x, y : longint);//堆操作
var
    i : longint;

begin
    inc(len);
    i := len;
    heap[i].node := x; heap[i].len := y;
    while i > 1 do
    begin
        if heap[i].len < heap[i >> 1].len then
        begin
            swap(heap[i].node, heap[i >> 1].node);
            swap(heap[i].len, heap[i >> 1].len);
            i := i >> 1;
        end else break;
    end;
end;

procedure pop;//堆操作
var
    i, x : longint;

begin
    heap[1] := heap[len];
    dec(len);
    i := 1;
    while (i << 1) <= len do
    begin
        if ((i << 1) or 1 > len) or (heap[i << 1].len < heap[(i << 1) or 1].len) then
            x := i << 1 else x := (i << 1) or 1;
        if heap[i].len > heap[x].len then
        begin
            swap(heap[i].node, heap[x].node);
            swap(heap[i].len, heap[x].len);
            i := x;
        end else break;
    end;
end;

procedure dijkstra;
var
    i : longint;
    u, v, l : longint;

begin
    for i := 2 to n do dis[i] := maxlongint >> 1;
    fillchar(vis, sizeof(vis), 0);
    len := 0;
    push(1, 0);
    while len > 0 do
    begin
        u := heap[1].node; l := heap[1].len;
        pop;
        if vis[u] then continue;
        vis[u] := true;
        i := head[u];
        while i <> 0 do
        begin
            v := edge[i].t;
            if dis[v] > dis[u] + edge[i].len then
            begin
                dis[v] := dis[u] + edge[i].len;
                push(v, dis[v]);
            end;
            i := edge[i].next;
        end;
    end;
end;

function dp(u, l : longint) : int64;
//这里稍微有些变动
//l表示余额为l
var
    i, v : longint;
    sum : int64;

begin
    if (l < 0) or (l > k) then exit(0);
    if flag[u][l] then exit(-1);
    if f[u][l] <> -1 then exit(f[u][l]);
    flag[u][l] := true;
    dp := 0;
    i := head2[u];
    while i <> 0 do
    begin
        v := edge2[i].t;
        sum := dp(v, dis[u] + l - dis[v] - edge2[i].len);
        //
        if sum = -1 then exit(-1);
        dp := (dp + sum) mod p;
        i := edge2[i].next;
    end;
    if (u = 1) and (l = 0) then inc(dp);
    flag[u][l] := false;
    f[u][l] := dp;
end;

procedure work;
var
    i : longint;
    ans, sum : int64;

begin
    dijkstra;
    fillchar(f, sizeof(f), 255);
    fillchar(flag, sizeof(flag), 0);
    ans := 0;
    for i := 0 to k do
    begin
        sum := dp(n, i);
        if sum = -1 then//-1的情况,直接再见
        begin
            writeln(-1);
            exit;
        end;
        ans := (ans + sum) mod p;
    end;
    writeln(ans);
end;

begin
    readln(qnum);
    for q := 1 to qnum do
    begin
        init;
        work;
    end;
end.


猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/83278935