题目链接

题目

题目描述

最近《绝地求生:大逃杀》风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏。

在游戏中,皮皮和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快递。

当然,有些时候并不能堵桥,皮皮和毛毛会选择在其他的必经之路上蹲点。

K博士作为一个老年人,外加有心脏病,自然是不能玩这款游戏的,但是这并不能妨碍他对这款游戏进行一些理论分析,比如最近他就对皮皮和毛毛的战士很感兴趣。

游戏的地图可以抽象为一张 n 个点 m 条无向边的图,节点编号为 1 到 n ,每条边具有一个正整数的长度。

假定大魔王都会从 S 点出发到达 T 点( S 和 T 已知),并且只会走最短路,皮皮和毛毛会在 A 点和 B 点埋伏大魔王。

为了保证一定能埋伏到大魔王,同时又想留大魔王一条生路,皮皮和毛毛约定 A 点和 B 点必须满足:

\1. 大魔王所有可能路径中,必定会经过 A 点和 B 点中的任意一点

\2. 大魔王所有可能路径中,不存在一条路径同时经过 A 点和 B 点

K博士想知道,满足上面两个条件的 A,B 点对有多少个,交换 A,B 的顺序算相同的方案。

输入描述

第一行输入四个整数 n,m,S,T(\(1≤n≤5×10^4,1≤m≤5×10^4,1≤S,T≤n\)),含义见题目描述。

接下来输入 m 行,每行输入三个整数 u,v,w(\(1≤u,v≤n,1≤w≤10^9\))表示存在一条长度为 w 的边链接 u 和 v 。

输出描述

输出一行表示答案。

示例1

输入

7 7 1 7
1 2 2
2 4 2
4 6 2
6 7 2
1 3 2
3 5 4
5 7 2

输出

6

说明

合法的方案为 <2,3>,<2,4>,<4,3>,<4,5>,<6,3>,<6,5>。

备注

\(1≤n≤5×10^4,1≤m≤5×10^4,1≤w≤10^9\)

题解

知识点:最短路,拓扑排序,计数dp。

这道题分几步走:

  1. 跑正反两次最短路,得到 \(f\) ,再求经过每个点 \(u\) 的最短路条数 \(ff[u] = f[0][u]\cdot f[1][u]\) ,不在最短路上的点应为 \(0\) 。同时,记录方案数到点的映射 \(mp[ff[u]][u] = 1\) 方便最后统计。注意不在最短路上的点也要统计,即 \(mp[0][u] = 1\) ,因为两个点可以有一个点不在最短路上,而另一个点通过了所有最短路。因为方案数本身过大,所以取了模,虽然很玄学,但能过。
  2. 根据第一步得到的 \(dis\) 新建一个最短路DAG图,即只包括最短路上的边且是单向的。在最短路图上跑一边拓扑排序得到拓扑序。对于每个点 \(u\) ,根据拓扑序求出可以从起点开始经过哪些点到达 \(tran[0][u]\),以及拓扑逆序求出经过哪些点到达终点 \(tran[1][u]\) 。这部分用bitset 实现刚刚好。
  3. 统计对于每个点 \(u\) 满足以下两个条件的点 \(v\) :\(S\) 经过 \(u\) 到 \(T\) 都不可能被经过,并且 \(ff[u] + ff[v] = ff[T]\) 。前者保证 \(u,v\) 不出现在一条最短路上满足条件2,后者保证 \(u,v\) 不重不漏的划分了所有路径,即从 \(S\) 到 \(T\) 必定经过 \(u,v\) 满足条件1。

最后,因为交换A,B顺序算一个答案,所以统计的答案除以 \(2\) 。

注意,有可能 \(S\) 不能到达 \(T\) ,即 \(ff[T] = 0\) ,此时应该A,B可以是任意点,答案是 \(\frac{n(n-1)}{2}\) 直接输出。因为最短路图建不成,所以不能继续走后面的步骤,应该直接输出。

时间复杂度 \(O((n+m)\log m) + O(n+m) + O(nm)\)

空间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
#define ll long long using namespace std; template<class T>
struct Graph {
struct edge {
int v, nxt;
T w;
};
int idx;
vector<int> h;
vector<edge> e; Graph(int n, int m) :idx(0), h(n + 1), e(m + 1) {}
void init(int n) {
idx = 0;
h.assign(n + 1, 0);
} void add(int u, int v, T w) {
e[++idx] = edge{ v,h[u],w };
h[u] = idx;
}
};
const int N = 50007, M = 50007 << 1, mod = 1e9 + 7;
Graph<int> g(N, M), g2(N, M);
int n, m; vector<vector<ll>> dis(2, vector<ll>(N));
vector<vector<int>> f(2, vector<int>(N));
vector<int> ff(N);
map<int, bitset<N>> mp; void dijkstra(int st, vector<ll> &dis, vector<int> &f) {
dis.assign(n + 1, 0x3f3f3f3f3f3f3f3f);
vector<bool> vis(n + 1, false);
struct node {
int v;
ll w;
bool operator<(const node &a)const {
return w > a.w;
}
};
priority_queue<node> pq; dis[st] = 0;
f[st] = 1;
pq.push(node{ st,0 });
while (!pq.empty()) {
int u = pq.top().v;
pq.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v, w = g.e[i].w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
pq.push(node{ v,dis[v] });
f[v] = f[u];
}
else if (dis[v] == dis[u] + w) {
f[v] = (f[v] + f[u]) % mod;
}
}
}
} vector<int> topo;
void toposort() {
vector<int> deg(n + 1, 0);
queue<int> q;
for (int i = 1;i <= g2.idx;i++) deg[g2.e[i].v]++;
for (int i = 1;i <= n;i++) if (!deg[i]) q.push(i);
while (!q.empty()) {
int u = q.front();
topo.push_back(u);
q.pop();
for (int i = g2.h[u];i;i = g2.e[i].nxt) {
int v = g2.e[i].v;
deg[v]--;
if (!deg[v]) q.push(v);
}
}
} bitset<N> tran[2][N]; int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int S, T;
cin >> n >> m >> S >> T;
for (int i = 1;i <= m;i++) {
int u, v, w;
cin >> u >> v >> w;
g.add(u, v, w);
g.add(v, u, w);
} //分别得到S,T到各个点的最短路和方案数
dijkstra(S, dis[0], f[0]);
dijkstra(T, dis[1], f[1]); if (f[0][T] == 0)//注意判断不连通的情况(等价于f[1][s]==0)
{
cout << 1LL * n * (n - 1) / 2 << '\n';//A可能情况*B可能情况/2(防止重复)
return 0;
} //在最短路上的点计算总条数,其他为0;并且统计一个方案数对应的点,方案数取模因为太大,不会被卡
for (int u = 1;u <= n;u++) {
if (dis[0][u] + dis[1][u] == dis[0][T])
ff[u] = 1LL * f[0][u] * f[1][u] % mod;
mp[ff[u]][u] = 1;
} //用最短路上的边建图
for (int u = 1;u <= n;u++) {
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v, w = g.e[i].w;
if (dis[0][u] + dis[1][v] + w == dis[0][T]) g2.add(u, v, w);
}
} //得到最短路遍历顺序
toposort(); //分别获得起点传递,终点逆传递
for (int i = 0;i < n;i++) {
int u = topo[i];
tran[0][u][u] = 1;
for (int j = g2.h[u];j;j = g2.e[j].nxt) {
int v = g2.e[j].v;
tran[0][v] |= tran[0][u];
}
}
for (int i = n - 1;i >= 0;i--) {
int u = topo[i];
tran[1][u][u] = 1;
for (int j = g2.h[u];j;j = g2.e[j].nxt) {
int v = g2.e[j].v;
tran[1][u] |= tran[1][v];
}
} //计算答案:两点不能在同一条最短路上,即g2中没有传递性;最短路至少通过一点,即两点方案数之和等于总方案数
ll ans = 0;
for (int u = 1;u <= n;u++) {
ans += ((~(tran[0][u] | tran[1][u])) & mp[(ff[T] - ff[u] + mod) % mod]).count();
}
cout << ans / 2 << '\n';
return 0;
}

NC14501 大吉大利,晚上吃鸡!的更多相关文章

  1. GMA Round 1 大吉大利,晚上吃鸡

    传送门 大吉大利,晚上吃鸡 新年走亲访友能干点啥呢,咱开黑吃鸡吧. 这里有32个人,每个人都可能想玩或者不想玩,这样子一共有$2^{32}$种可能.而要开黑当然得4人4人组一队(四人模式),所以说如果 ...

  2. [BZOJ5109]大吉大利,晚上吃鸡!

    [BZOJ5109]大吉大利,晚上吃鸡! 题目大意: 一张\(n(n\le5\times10^4)\)个点\(m(m\le5\times10^4)\)条边的无向图,节点编号为\(1\)到\(n\),边 ...

  3. 【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃鸡! 最短路+拓扑排序+DP

    [BZOJ5109][CodePlus 2017]大吉大利,晚上吃鸡! Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏 ...

  4. bzoj5109: [CodePlus 2017]大吉大利,晚上吃鸡!

    Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏中,皮皮 和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快 ...

  5. 「CodePlus 2017 11 月赛」大吉大利,晚上吃鸡!(dij+bitset)

    从S出发跑dij,从T出发跑dij,顺便最短路计数. 令$F(x)$为$S$到$T$最短路经过$x$的方案数,显然这个是可以用$S$到$x$的方案数乘$T$到$x$的方案数来得到. 然后第一个条件就变 ...

  6. BZOJ5109 CodePlus 2017大吉大利,晚上吃鸡!(最短路+拓扑排序+bitset)

    首先跑正反两遍dij求由起点/终点到某点的最短路条数,这样条件一就转化为f(S,A)*f(T,A)+f(S,B)*f(T,B)=f(S,T).同时建出最短路DAG,这样图中任何一条S到T的路径都是最短 ...

  7. 「CodePlus 2017 11 月赛」大吉大利,晚上吃鸡!

    n<=50000,m<=50000的图,给s和t,问有多少点对$(a,b)$满足 嗯. 不会. 首先最短路DAG造出来,然后两个条件转述一下:条件一,$N_a$表示从s到t经过a的路径,$ ...

  8. [Code+#1]大吉大利,晚上吃鸡!

    输入输出样例 输入样例#1: 7 7 1 7 1 2 2 2 4 2 4 6 2 6 7 2 1 3 2 3 5 4 5 7 2 输出样例#1: 6 输入样例#2: 5 5 1 4 1 2 1 1 3 ...

  9. [BZOJ5109/CodePlus2017]大吉大利,晚上吃鸡!

    Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏中,皮皮和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快递 ...

  10. luogu4061 大吉大利,晚上吃鸡!

    链接 最短路径\(dag\),一道好题. 题目大意:求一张图中满足下列要求的点对\((i,j)\)数量: 所有最短路径必定会经过 \(i\) 点和 \(j\) 点中的任意一点. 不存在一条最短路同时经 ...

随机推荐

  1. Vue学习之--------监视属性(2022/7/10)

    文章目录 1.监视属性 1.1 监视属性--天气案例 1.1.1 基础知识 1.1.2 代码实例 1.1.2 测试效果 1.2 深度监视-天气案例 1.2.1 基础知识 1.2.2 代码实例 1.2. ...

  2. C# 6.0 添加和增强的功能【基础篇】

    C# 6.0 是在 visual studio 2015 中引入的.此版本更多关注了语法的改进,让代码更简洁且更具可读性,使编程更有效率,而不是和前几个版本一样增加主导性的功能. 一.静态导入 我们都 ...

  3. 十七、Job与Cronjob

    Job 与 Cronjob 一.Job ​Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束. 特殊说明: 1.spec.template 格式同 Pod ​2 ...

  4. 【深入浅出 Yarn 架构与实现】2-1 Yarn 基础库概述

    了解 Yarn 基础库是后面阅读 Yarn 源码的基础,本节对 Yarn 基础库做总体的介绍.并对其中使用的第三方库 Protocol Buffers 和 Avro 是什么.怎么用做简要的介绍. 一. ...

  5. 【题解】CF356A Knight Tournament

    题面传送门 本蒟蒻想练习一下并查集,所以是找并查集标签来这里的.写题解加深理解. 解决思路 自然,看到区间修改之类很容易想到线段树,但本蒟蒻线段树会写挂,所以这里就讲比较简单的并查集思路. 并查集的核 ...

  6. mysql忽略大小写配置

    #更改配置文件:vim /etc/my.cnf#添加此行在[mysqld]下lower_case_table_names=1​#重启服务systemctl restart mysqld  

  7. PS2023下载安装保姆级教程中文汉化完整版

    PS2023Windows安装教程退出安全软件①:下载PS2023安装包 ②:打开下载好的文件,鼠标右键把安装包解压③:打开解压好的"PS 24.0.0"文件夹,找到并选中&quo ...

  8. 使用 Go HTTP 框架 Hertz 进行 JWT 认证

    前言 上一篇文章简单介绍了一个高性能的 Go HTTP 框架--Hertz,本篇文章将围绕 Hertz 开源仓库的一个 demo,讲述如何使用 Hertz 完成 JWT 的认证与授权流程. 这里要说明 ...

  9. <四>理解空间配置器allocator, 优化STL 中的Vector

    .在上一节我们实现的 MyVector存在哪些问题? 问题1 现在有Student类 class Student{ public: Student(){cout<<"构造Stud ...

  10. 基于echarts的带流动光效的折线图-lowline-for-echarts使用记录

    起源 在技术群看到有人问这个react插件,带着好奇心看了一下. 标题:基于echarts的带流动光效的折线图 - 掘金 网址:https://juejin.cn/post/7090566240233 ...