题目链接

题目

题目描述

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

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

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

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. C++ 使用栈求解中缀、后缀表达式的值

    1. 前言 表达式求值对于有知识积累的你而言,可以通过认知,按运算符的优先级进行先后运算. 但对计算机而言,表达式仅是一串普通的信息而已,需要通过编码的方式告诉计算机运算法则,这个过程中栈起到了至关重 ...

  2. CSAPP实验attacklab

    attacklab 实验报告和答案文件都在 https://github.com/thkkk/attacklab

  3. Java并发编程 | Synchronized原理与使用

    Java提供了多种机制实现多线程之间有需要同步执行的场景需求.其中最基本的是Synchronized ,实现上使用对象监视器( Monitor ). Java中的每个对象都是与线程可以锁定或解锁的对象 ...

  4. 亚马逊云 RDB数据故障转移(多可用区)

    RDB关系数据库(Relational Database,RDB) 创建名为VPC for RDS的vpc 两个可用区,两组公内网 创建安全组 创建RDS数据库实例用的数据库子网组 创建RDS数据库实 ...

  5. 抓包分析 TCP 握手和挥手

    前言 首先需要明确的是 TCP 是一个可靠传输协议,它的所有特点最终都是为了这个可靠传输服务.在网上看到过很多文章讲 TCP 连接的三次握手和断开连接的四次挥手,但是都太过于理论,看完感觉总是似懂非懂 ...

  6. 论文笔记 - GRAD-MATCH: A Gradient Matching Based Data Subset Selection For Efficient Learning

    Analysis Coreset 是带有权重的数据子集,目的是在某个方面模拟完整数据的表现(例如损失函数的梯度,既可以是在训练数据上的损失,也可以是在验证数据上的损失): 给出优化目标的定义: $w^ ...

  7. 真正“搞”懂HTTP协议03之时间穿梭

    上一篇我们简单的介绍了一下DoD模型和OSI模型,还着重的讲解了TCP的三次握手和四次挥手,让我们在空间层面,稍稍宏观的了解了HTTP所依赖的底层模型,那么这一篇,我们来追溯一下HTTP的历史,看一看 ...

  8. JetBrains新产品Aqua——自动化测试开发工具(抢鲜体验)

    转载请注明出处️ 作者:测试蔡坨坨 原文链接:caituotuo.top/9a093c88.html 你好,我是测试蔡坨坨. 随着行业内卷越来越严重,自动化测试已成为测试工程师的必备技能,谈及自动化测 ...

  9. Terminal(oh-my-zsh) 美化

    如果你使用Mac进行开发,那么Terminal.app应该是你使用非常频繁的app了.初体验Terminal时你可能觉得单调乏味,阅读密密麻麻的内容也很费劲.但是如果你跟着我一起配置它,就会发现你平时 ...

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

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