5109: [CodePlus 2017]大吉大利,晚上吃鸡!

Time Limit: 30 Sec  Memory Limit: 1024 MB
Submit: 107  Solved: 57
[Submit][Status][Discuss]

Description

最近《绝地求生:大逃杀》风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏。在游戏中,皮皮
和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快递。当然,有些时候并不能堵桥,皮皮和毛
毛会选择在其他的必经之路上蹲点。K博士作为一个老年人,外加有心脏病,自然是不能玩这款游戏的,但是这并
不能妨碍他对这款游戏进行一些理论分析,比如最近他就对皮皮和毛毛的战士很感兴趣。【题目描述】游戏的地图
可以抽象为一张n个点m条无向边的图,节点编号为1到n,每条边具有一个正整数的长度。假定大魔王都会从S点出
发到达T点(S和T已知),并且只会走最短路,皮皮和毛毛会在A点和B点埋伏大魔王。
为了保证一定能埋伏到大魔王,同时又想留大魔王一条生路,皮皮和毛毛约定A点和B点必须满足:
1.大魔王所有可能路径中,必定会经过A点和B点中的任意一点
2.大魔王所有可能路径中,不存在一条路径同时经过A点和B点
K博士想知道,满足上面两个条件的A,B点对有多少个,交换A,B的顺序算相同的方案

Input

第一行输入四个整数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≤n≤5×10^4,1≤m≤5×10^4,1≤w≤10^9

Output

输出一行表示答案

Sample Input

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

Sample Output

6
【样例 1 解释】
合法的方案为 < 2, 3 >, < 2, 4 >, < 4, 3 >, < 4, 5 >, < 6, 3 >, < 6, 5 > 。

HINT

来自 CodePlus 2017 11 月赛,清华大学计算机科学与技术系学生算法与竞赛协会 荣誉出品。
Credit:idea/陈宇 命题/陈宇 验题/邢健开
Git Repo:https://git.thusaac.org/publish/CodePlus201711
本次比赛的官方网址:cp.thusaac.org
感谢腾讯公司对此次比赛的支持。

题目简述

给定一张有边权(边权全为正)的无向图, $n$ 个点 $m$ 条边,给定起点 $S$ 和终点 $T$ ,问有多少对 $A$ 和 $B$ 满足从 $S$ 到 $T$ 的任意最短路一定经过 $A$ 或者 $B$ ,但是不存在某条最短路同时经过 $A$ 和 $B$ 。

解法一

首先是最暴力的解法,枚举任意点对 $A$ 和 $B$ ,然后删掉 $A$ 和 $B$ ,看看最短距离是否会变长,然后查看 $dis(S, A) + dis(A, B) + dis(B, T)$ 是否和 $dis(S, T)$ 相等,其中 $dis(X, Y)$ 表示原图中点 $X$ 到 $Y$ 的最短距离,由此可以判断枚举的 $A$ 和 $B$ 是否是合法的点对。
其中, $dis(X, Y)$ 可以用floyd求解
时间复杂度: $O \left(n3 \right)$
期望得分: $30$

解法二

首先,虽然题目中给定的是无向图,但是实际上我们可以先从 $S$ 出发求一遍最短路,然后问题变成了:“在有向无环图上,求有多少个满足条件的点对 $A,B$ ,满足从 $S$ 到 $T$ 的所有路径一定经过 $A,B$ 其中一点,并且不存在路径同时经过 $A,B$ ”。
求解这到题目的一个关键点在于: 满足条件的点对 $A,B$ 具有特点:从 $S$ 到 $A$ 的方案数 $\times$ 从 $A$ 到 $T$ 的方案数 + 从 $S$ 到 $B$ 的方案数 $\times$ 从 $B$ 到 $T$ 的方案数 $=$ 从 $S$ 到 $T$ 的方案数。
所以在有向无环图上用动态规划求解路径条数,再去掉 $A$ 可以到达 $B$ 或 $B$ 可以到达 $A$ 的情况即可求解这到题目。
PS:方案数可能会爆掉怎么办?可以对方案数求余一个大整数,如果觉得不够的话可以求余两个大整数。
时间复杂度: $O \left(n2+nm \right)$
期望得分: $60$

解法三
在解法二中,定义 $F(X) = $ 从 $S$ 到 $X$ 的方案数 $\times$ 从 $X$ 到 $T$ 的方案数 = 从 $S$ 经过 $X$ 到达 $T$ 的方案数,所以满足条件的点对 $A,B$ 为:
$F(A) + F(B) = F(T)$
$A$ 和 $B$ 不能相互到达
对于条件 $1$ ,我们可以使用数据结构进行优化(使用std::map即可),而对于条件 $2$ ,我们可以使用 bitset 位压 $32$ 或者 $64$ 位进行加速,使得最终时间和空间都能够承受。
时间复杂度: $O\left(n \log{n}+\frac{nm}{w}\right)$,其中 $w$ 是位压的字长。
期望得分: $100$

题解已经说的很清楚了,标算是hash+压位实现,但是考场上没时间写的话,就用map代替hash,用bitset代替压位,当然代价是常数大了好多倍。

这里说一下传递闭包

所谓传递性,可以这样理解:对于一个节点i,如果j能到i,i能到k,那么j就能到k。求传递闭包,就是把图中所有满足这样传递性的节点都弄出来,计算完成后,我们也就知道任意两个节点之间是否相连。 
传递闭包的计算过程一般可以用Warshell算法描述: 
For 每个节点i Do 
    For 每个节点j Do 
    If j能到i Then 
        For 每个节点k Do 
        a[j, k] := a[j, k] Or ( a[j, i] And a[ i, k] ) 
其中a数组为布尔数组,用来描述两个节点是否相连,可以看做一个无权图的邻接矩阵。可以看到,算法过程跟Floyd很相似,三重循环,枚举每个中间节点。只不过传递闭包只需要求出两个节点是否相连,而不用求其间的最短路径长。

上面的方法可以求出任意两个点是否连通,不过点数不多的时候一般我们可以压位或者bitset,然后就成了:

(实际上是lnk[i][e[j].s]但是可以将数组压缩成一维,也就是:)

rep(i,1,n-1) for (int j=1; j<=cnt; j++) lnk[e[j].s]|=lnk[e[j].t];

这样后面的按照题解做就好了。代码还是比较繁琐的。

  1. #include<map>
  2. #include<queue>
  3. #include<bitset>
  4. #include<cstdio>
  5. #include<algorithm>
  6. #define rep(i,l,r) for (int i=l; i<=r; i++)
  7. typedef long long ll;
  8. using namespace std;
  9.  
  10. const int N=;
  11. int n,m,u,v,w,cnt,S,T,h[N];
  12. struct E{ int to,nxt,w; }e[N<<];
  13. queue<int> Q;
  14. int vis[N],iT[N]; ll dis[N],rdis[N];
  15.  
  16. void add(int x,int y,int z){
  17. e[++cnt]=(E){y,h[x],z}; h[x]=cnt;
  18. e[++cnt]=(E){x,h[y],z}; h[y]=cnt;
  19. }
  20.  
  21. void spfa(ll dis[],int S){
  22. rep(i,,n) dis[i]=1ll<<,vis[i]=;
  23. Q.push(S); vis[S]=; dis[S]=;
  24. while (!Q.empty()){
  25. int x=Q.front(); Q.pop(); vis[x]=;
  26. for (int i=h[x],k; i; i=e[i].nxt)
  27. if (dis[k=e[i].to]>dis[x]+e[i].w){
  28. dis[k]=dis[x]+e[i].w;
  29. if (!vis[k]) vis[k]=,Q.push(k);
  30. }
  31. }
  32. }
  33.  
  34. namespace G{
  35. int h[N],rh[N],d[N],rd[N],cnt;
  36. ll f[N],g[N];
  37. queue<int>Q;
  38. bitset<N>lnk[N],rlnk[N];
  39. map<ll,bitset<N> >M;
  40. struct E{ int s,t,nxt; }e[N<<];
  41.  
  42. void add(int x,int y){
  43. e[++cnt]=(E){x,y,h[x]}; h[x]=cnt; d[y]++;
  44. e[++cnt]=(E){y,x,rh[y]}; rh[y]=cnt; rd[x]++;
  45. }
  46.  
  47. ll solve(){
  48. f[S]=; g[T]=; Q.push(S);
  49. while (!Q.empty()){
  50. int x=Q.front(); Q.pop();
  51. for (int i=h[x]; i; i=e[i].nxt){
  52. f[e[i].t]+=f[x];
  53. if (!(--d[e[i].t])) Q.push(e[i].t);
  54. }
  55. }
  56. Q.push(T);
  57. while (!Q.empty()){
  58. int x=Q.front(); Q.pop();
  59. for (int i=rh[x]; i; i=e[i].nxt){
  60. g[e[i].t]+=g[x];
  61. if (!(--rd[e[i].t])) Q.push(e[i].t);
  62. }
  63. }
  64. rep(i,,n) lnk[i].set(i),rlnk[i].set(i);
  65. rep(i,,n-) for (int j=; j<=cnt; j+=) lnk[e[j].s]|=lnk[e[j].t];
  66. rep(i,,n-) for (int j=; j<=cnt; j+=) rlnk[e[j].s]|=rlnk[e[j].t];
  67. rep(i,,n) M[1ll*f[i]*g[i]].set(i);
  68. ll res=;
  69. rep(i,,n){
  70. if (!M.count(f[T]-f[i]*g[i])) continue;
  71. res+=(M[f[T]-f[i]*g[i]] & (~lnk[i]) & (~rlnk[i])).count();
  72. }
  73. return res>>;
  74. }
  75. }
  76.  
  77. int main(){
  78. freopen("bzoj5109.in","r",stdin);
  79. freopen("bzoj5109.out","w",stdout);
  80. scanf("%d%d%d%d",&n,&m,&S,&T);
  81. rep(i,,m) scanf("%d%d%d",&u,&v,&w),add(u,v,w);
  82. spfa(dis,S); spfa(rdis,T);
  83. rep(i,,n) if (dis[i]+rdis[i]==dis[T]) iT[i]=;
  84. rep(x,,n) if (iT[x])
  85. for (int i=h[x]; i; i=e[i].nxt)
  86. if (dis[e[i].to]==dis[x]+e[i].w && iT[e[i].to]) G::add(x,e[i].to);
  87. printf("%lld\n",G::solve());
  88. return ;
  89. }

[BZOJ5109][LOJ #6252][P4061][CodePlus 2017 11月赛]大吉大利,今晚吃鸡!(最短路+拓扑排序+传递闭包+map+bitset(hash+压位))的更多相关文章

  1. [LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

    Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不 ...

  2. [LOJ 6248]「CodePlus 2017 11 月赛」晨跑

    Description “无体育,不清华”.“每天锻炼一小时,健康工作五十年,幸福生活一辈子” 在清华,体育运动绝对是同学们生活中不可或缺的一部分.为了响应学校的号召,模范好学生王队长决定坚持晨跑.不 ...

  3. loj #6250. 「CodePlus 2017 11 月赛」找爸爸

    #6250. 「CodePlus 2017 11 月赛」找爸爸 题目描述 小 A 最近一直在找自己的爸爸,用什么办法呢,就是 DNA 比对. 小 A 有一套自己的 DNA 序列比较方法,其最终目标是最 ...

  4. [CodePlus 2017 11月赛&洛谷P4058]木材 题解(二分答案)

    [CodePlus 2017 11月赛&洛谷P4058]木材 Description 有 n棵树,初始时每棵树的高度为 Hi ,第 i棵树每月都会长高 Ai.现在有个木料长度总量为 S的订单, ...

  5. [CodePlus 2017 11月赛]晨跑 题解(辗转相除法求GCD)

    [CodePlus 2017 11月赛]晨跑 Description "无体育,不清华"."每天锻炼一小时,健康工作五十年,幸福生活一辈子".在清华,体育运动绝 ...

  6. [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞

    [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞 试题描述 到河北省 见斯大林 / 在月光下 你的背影 / 让我们一起跳舞吧 うそだよ~ 河北省怎么可能有 Stalin. ...

  7. LOJ6252. 「CodePlus 2017 11 月赛」大吉大利,晚上吃鸡! 最短路+bitset

    题目传送门 https://loj.ac/problem/6252 https://lydsy.com/JudgeOnline/problem.php?id=5109 题解 首先跑最短路,只保留 \( ...

  8. 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

    学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2 ...

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

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

随机推荐

  1. 【BZOJ】2099: [Usaco2010 Dec]Letter 恐吓信

    [题意]给定长度为n和m的两个字符串S和T,要求在字符串S中取出若干段拼成T(可重复取),求最小段数,n,m<=50000. [算法]后缀自动机 || 后缀数组 [题解]对串S建SAM,然后在上 ...

  2. python笔记之BytesIO

    1. 什么是BytesIO BytesIO与StringIO类似,不同的是StringIO只能存放string,BytesIO是用来存放bytes的,它提供了在内存中读写字节的能力. 即在内存中读写字 ...

  3. 重载jquery on方法实现click事件在移动端的快速响应

    额,这个标题取的还真是挺装的... 其实我想表达的是jquery click事件如何在移动端自动转换成touchstart事件. 因为移动端click事件会比touchstart事件慢几拍 移动设备某 ...

  4. ueditor和thinkphp框架整合修改版

    基于tp官网上的一篇文章修改的  因为tp中所有目录其实都是性对于入口文件的 在原来的基础上略做修改后 已经做到 无论项目放在www下的任何位置 图片在编辑器中回填后都能正常显示! http://fi ...

  5. Coursera在线学习---第四节.过拟合问题

    一.解决过拟合问题方法 1)减少特征数量 --人为筛选 --靠模型筛选 2)正则化(Regularization) 原理:可以降低参数Θ的数量级,使一些Θ值变得非常之小.这样的目的既能保证足够的特征变 ...

  6. 72.xilinx vivado zynq vdma仿真及应用详解(一)

    很多人用zynq平台做视频图像开发,但是对vdma了解比较少,上手起来稍微有些困难,我针对这一现象,做了一个基于vivado和modelsim的仿真和应用测试工程,并写篇文章做些介绍,希望能对大家有帮 ...

  7. linux设备驱动模型-浅析-转

    1.  typeof typeof并非ISO C的关键字,而是gcc对C的一个扩展.typeof是一个关键字(类似sizeof),用于获取一个表达式的类型. 举个简单的例子: char tt; typ ...

  8. Spring中的@Transactional事务注解

    事务注解方式 @Transactional 当标于类前时, 标示类中所有方法都进行事物处理 , 例子: @Transactional public class TestServiceBean impl ...

  9. log4j生成日志

    Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护进程等:我们也可 ...

  10. java网络编程三次握手四次挥手

    第一次握手:client设置syn=1,随机产生一个序列号seq=x,将数据包发送到server.client进入syn_send状态, 等待server确认. 第二次握手:server查看clien ...