首页 > 其他分享 >NOIP2017 逛公园 记忆化搜索|dp(已过hack数据)

NOIP2017 逛公园 记忆化搜索|dp(已过hack数据)

时间:2022-11-06 00:00:52浏览次数:92  
标签:NOIP2017 std dist int memset hack sizeof dp

30pts

可以发现,\(k=0\)的情况下,问题转化为最短路计数,即从起点\(s\)到每个点有多少最短路。

跑最短路的时候顺便维护\(ans[u]\),表示从\(s\)到\(u\)的最短路方案,讨论如下:

①当\(dist[v]>dist[u]+val[u][v]\)时,\(ans[v]=ans[u];\)

②当\(dist[v]=dist[u]+val[u][v]\)时,\(ans[v]+=ans[u]。\)

答案即是\(ans[n]\)。

100pts

1.动态规划

首先不考虑\(w=0\)的边的处理。

容易发现,\(k\leq50\),故考虑枚举k的值,问题转化为求解每个k下的最短路方案数个数。

令\(dp[u][k]\)表示当前在第\(u\)个点,最短路距离为\(dist[u]+k\)的方案数。答案即为\(\sum_{i=0}^{50} dp[n][i]\)。

想要得知终点的\(dp\)方程状态,那么从起点建正图来推状态是比较复杂的,故不妨从终点\(n\)倒推状态。

考虑\(dp[v][x]->dp[u][k]\)的状态转移,发现\(k\)可以由\(v\)转移过来:

\[(dist[v]+x)+val[v][u]=dist[u]+k \]

\[k=dist[v]+x+val[v][u]-dist[u] \]

\[dp[u][k]=\sum_vdp[v][dist[v]+x+val[v][u]-dist[u]] \]

复杂度\(O(km)\)

2.无穷多解处理

可以发现,若通过的道路上出现一个权值均为0的环,那一定会出现无穷多解。但有零环不代表一定存在无穷解

考虑一个特殊数据:

image

显然的是,2-4的零环并不影响最短路方案,但是4-5-6的零环会产生无穷多解。

故我们首先用\(tarjan\)找出零环,对这条零环上的点\(u\)(任一点即可,对答案的贡献一致)进行如下判断\(^{[1]}\):

\[dist[u]+dist2[u]\leq dis[n]+k \]

其中\(dist[u]\)代表\(1-u\)的最短路,\(dist2[u]\)代表\(u-n\)的最短路。

那么说明这条零环位于最短路上,说明存在无穷解。

综上:

对整张图分别正反建边,并跑一遍最短路;跑一遍\(tarjan\)寻找零环,判断零环是否导致无穷解即可。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stack>

int T;
int n, m, k, p;
int X, Y, Z;
int ans;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;

int nxt[maxn << 1], to[maxn << 1], head[maxn], val[maxn << 1];
int nxt2[maxn << 1], to2[maxn << 1], head2[maxn], val2[maxn << 1];
int nxt3[maxn << 1], to3[maxn << 1], head3[maxn], val3[maxn << 1];
int dis[maxn], dis2[maxn];
int tot1, tot2, tot3;
bool vis1[maxn << 1], vis2[maxn << 1];

inline void add1(int a, int b, int c) {
    to[++tot1] = b;
    val[tot1] = c;
    nxt[tot1] = head[a];
    head[a] = tot1;
}

inline void add2(int a, int b, int c) {
    to2[++tot2] = b;
    val2[tot2] = c;
    nxt2[tot2] = head2[a];
    head2[a] = tot2;
}

inline void add3(int a, int b, int c) {
    to3[++tot3] = b;
    val3[tot3] = c;
    nxt3[tot3] = head3[a];
    head3[a] = tot3;
}


inline void dijsktra() {
    memset(dis, inf, sizeof(dis));
    memset(vis1, 0, sizeof(vis1));
    dis[1] = 0;
    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, std::greater<std::pair<int, int> > > q;
    q.push(std::make_pair(0, 1));
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis1[u]) continue;
        vis1[u] = 1;
        for (int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            //if (vis[v]) continue;
            if (dis[v] > dis[u] + val[i]) {
                dis[v] = dis[u] + val[i];
                q.push(std::make_pair(dis[v], v));
            }
        }
    }
}

inline void oppodijsktra() {
    memset(dis2, inf, sizeof(dis2));
    memset(vis2, 0, sizeof(vis2));
    dis2[n] = 0;
    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, std::greater<std::pair<int, int> > > q2;
    q2.push(std::make_pair(0, n));
    while (!q2.empty()) {
        int u = q2.top().second;
        q2.pop();
        if (vis2[u]) continue;
        vis2[u] = 1;
        for (int i = head2[u]; i; i = nxt2[i]) {
            int v = to2[i];
            //if (vis[v]) continue;
            if (dis2[v] > dis2[u] + val2[i]) {
                dis2[v] = dis2[u] + val2[i];
                q2.push(std::make_pair(dis2[v], v));
            }
        }
    }
}

int dfn[maxn], low[maxn];
bool vis[maxn], isZero[maxn];//isZero判断是否为零环上点
int cnt;
std::stack<int> st;

inline void tarjan(int x) {
    dfn[x] = low[x] = ++cnt;
    st.push(x);
    vis[x] = 1;

    for (int i = head3[x]; i; i = nxt3[i]) {
        int v = to3[i];
        if (!dfn[v]) {
            tarjan(v);
            low[x] = std::min(low[x], low[v]);
        } else if (vis[v]) {
            low[x] = std::min(low[x], dfn[v]);
        }
    }

    if (dfn[x] == low[x]) {
        int sum = 0, temp;
        do {
            temp = st.top();
            st.pop();
            vis[temp] = 0;
            sum++;

        } while (temp != x);
        isZero[x] = 1;
        if (sum == 1) isZero[x] = 0;//0点(且已知不存在自环)不属于零环
    }
}

bool flag = 0;

int dp[maxn][55];

int dfs(int x, int k) {
    if (dp[x][k] != -1) return dp[x][k];
    dp[x][k] = 0;
    for (int i = head2[x]; i; i = nxt2[i]) {
        int v = to2[i], w = val2[i];
        int temp = dis[x] + k - (dis[v] + w);
        if (temp < 0) continue;
        dp[x][k] = (dp[x][k] + dfs(v, temp)) % p;
    }
    return dp[x][k];
}

inline void clear() {
    memset(nxt, 0, sizeof(nxt));
    memset(to, 0, sizeof(to));
    memset(val, 0, sizeof(val));

    memset(nxt2, 0, sizeof(nxt2));
    memset(to2, 0, sizeof(to2));
    memset(val2, 0, sizeof(val2));

    memset(nxt3, 0, sizeof(nxt3));
    memset(to3, 0, sizeof(to3));
    memset(val3, 0, sizeof(val3));

    for (int i = 1; i <= n; i++)
        head[i] = head2[i] = head3[i] = low[i] = dfn[i] = isZero[i] = vis[i] = 0;
    ans = flag = 0;
    tot1 = tot2 = tot3 = 0;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d%d", &n, &m, &k, &p);
        clear();
        while (m--) {
            scanf("%d%d%d", &X, &Y, &Z);
            add1(X, Y, Z);
            add2(Y, X, Z);
            if (Z == 0) add3(X, Y, Z);
        }
        dijsktra();
        oppodijsktra();

        for (int i = 1; i <= n; i++)
            if (!dfn[i]) tarjan(i);
        for (int i = 1; i <= n; i++) {
            if (isZero[i]) {
                if (dis[i] + dis2[i] <= dis[n] + k) {
                    flag = 1;
                    break;
                }
            }
        }
        if (flag) {
            printf("-1\n");
            continue;
        }//判断会产生-1解的0环
        memset(dp, -1, sizeof(dp));
        dp[1][0] = 1;
        for (int i = 0; i <= k; i++)
            ans = (ans + dfs(n, i)) % p;
        printf("%d\n", ans);
    }
    return 0;
}

参考:

[1].^零环与无穷多解关系的证明与思路

标签:NOIP2017,std,dist,int,memset,hack,sizeof,dp
From: https://www.cnblogs.com/MrWangnacl/p/16861743.html

相关文章

  • JavaScript修改修改图片dpi
    欢迎关注前端早茶,与广东靓仔携手共同进阶​​​​前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~ 一、原理changeDPI提供了2个实用函数,可以更改画布生成的图像的dpi,无......
  • JavaScript修改修改图片dpi
    欢迎关注前端早茶,与广东靓仔携手共同进阶​​​​前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~ 一、原理changeDPI提供了2个实用函数,可以更改画布生成的图像的dpi,无......
  • 数位dp例题
    怎么感觉这种dp有点过于板子 1. 某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如12245 问区间【l,r】内有多少个不降数。#include<ios......
  • 腾讯云Linux轻量服务器使用宝塔面板一键部署WordPress个人博客教程
    WordPress作为动态博客的代表,至今已经有十几年历史,而且一直在更新发展中,功能强大,插件和主题丰富,WordPress搭建使用也很方便。作为个人站长和博主,很多都是从WordPress入门......
  • Android实现UDP通信
    TCP和UDP的不同上次我们讲的是TCP的socket,他们之间的不同在于,tcp要等待客户端的接入,然后获得客户端socket然后进行IO操作,udp直接传送数据即可  图片来源:面试官:说说U......
  • Solution-P7650 [BalticOI 2007 Day 1] Ranklist Sorting(DP)
    容易发现一条性质:每个人最多只会被移动一次。说明人只有两种:移动的和不移动的。考虑枚举所有不移动的人,并最优化其它人的移动顺序。最开始第\(i\)个人的起点为\(i\),终......
  • 深入浅出DPDK 电子书 pdf
    作者:朱河清/梁存铭/胡雪焜出版社:机械工业出版社 链接:深入浅出DPDK  近年来,随着网络技术的不断创新和市场的发展,越来越多的网络设备基础架构开始向基于通......
  • EasyCVR国标GB28181协议接入下的TCP和UDP模式说明及差异
    有用户在使用我们的平台时,经常会出现对于端口的疑问,同时也不了解端口的差别。今天我们来解释说明下EasyCVR平台关于国标GB28181协议接入下的TCP和UDP模式的说明及差异。......
  • 基于 Docker 构建轻量级 CI 系统:Gitea 与 Woodpecker CI 集成
    WoodpeckerCI是一个由社区维护的DroneCI分支,使用ApacheLicense2.0许可证发布。社区版进一步扩展了pipeline的功能特性、支持对文件路径设置pipeline执行条件,并......
  • UDP、TCP
    /**UDP协议:*1.面向无连接,不可靠协议*2.数据会被分包,数据限制在64k*3.不需要建立连接,速度快*比如:聊天数据共享,视频会议时数据传输.*TCP协议:*1.必须建......