二进制分组也可以说是一种比较优美的拆贡献方式吧?

Description

菩萨为行,福慧双修,智人得果,不忘其本。
——唐朠立《大慈恩寺三藏法师传》
有才而知进退,福慧双修,这才难得。
——乌雅氏
如何福慧双修?被太后教导的甄嬛徘徊在御花园当中。突然,她发现御花园中的花朵全都是红色和蓝色的。她冥冥之中得到了响应:这就是指导她如何福慧双修的! 现在御花园可以看作是有N块区域,M条小路,两块区域之间可通过小路连接起来。现在甄嬛站在1号区域,而她需要在御花园中绕一绕,且至少经过1个非1号区 域的区域。但是恰好1号区域离碎玉轩最近,因此她最后还是要回到1号区域。由于太后教导她要福慧双修,因此,甄嬛不能走过任何一条她曾经走过的路。但是, 御花园中来往的奴才们太多了,而且奴才们前行的方向也不一样,因此甄嬛在走某条小路的时候,方向不同所花的时间不一定一样。天色快暗了,甄嬛需要尽快知道 至少需要花多少时间才能学会如何福慧双修。如果甄嬛无法达到目的,输出“-1”。

Input

第一行仅2个正整数n,m,意义如题。
接下来m行每行4个正整数s,t,v,w,其中s,t为小路所连接的两个区域的编号,v为甄嬛从s到t所需的时间,w为甄嬛从t到s所需的时间。数据保证无重边。

Output

仅一行,为甄嬛回到1号区域所需的最短时间,若方案不存在,则输出-1

HINT

[样例解释]

对于第一个数据:路径为1->2->3->1,所需时间为8,而1->3->2->1所花时间为9。因此答案为8.

[数据范围与约定]

对于40%的数据:n<=1,000; m<=5,000

对于100%的数据:1<=n<=40,000; 1<=m<=100,000; 1<=v,w<=1,000


题目分析

首先考虑一些性质。对于不与1相连的边,由于边权为正,所以它们是一定不会经过多次的,那么这个限制相当于就去掉了。接下去的问题就出在与1相连的边这里,因为可能从同一条边出入1点最优。

朴素做法:指定一条出边和一条入边,每次计算答案。

考虑答案的出入边$i$和$j$,那么$i,j$一定有一个二进制位不同。于是我们枚举不同的二进制位,将该位为0/1的两类边分别分成出边/入边集合,做完一次dij就枚举入边统计答案。

时间复杂度:$O(n\log^2n)$.

后话:这个做法复杂度在这两题确实有点大了。两题差不多都拿了倒数前20吧。

  1. #include<bits/stdc++.h>
  2. const int maxn = ;
  3. const int maxm = ;
  4.  
  5. struct Edge
  6. {
  7. int v,c;
  8. Edge(int a=, int b=):v(a),c(b) {}
  9. }edges[maxm];
  10. struct node
  11. {
  12. int x,d;
  13. node(int a=, int b=):x(a),d(b) {}
  14. bool operator < (node a) const
  15. {
  16. return d > a.d;
  17. }
  18. };
  19. int n,m,ans,dis[maxn],dfn[maxn],tim;
  20. int edgeTot,head[maxn],nxt[maxm],tag[maxm];
  21. std::priority_queue<node> q;
  22.  
  23. int read()
  24. {
  25. char ch = getchar();
  26. int num = , fl = ;
  27. for (; !isdigit(ch); ch=getchar())
  28. if (ch=='-') fl = -;
  29. for (; isdigit(ch); ch=getchar())
  30. num = (num<<)+(num<<)+ch-;
  31. return num*fl;
  32. }
  33. void addedge(int u, int v, int c)
  34. {
  35. edges[++edgeTot] = Edge(v, c), nxt[edgeTot] = head[u], head[u] = edgeTot;
  36. }
  37. void dijkstra()
  38. {
  39. memset(dis, 0x3f3f3f3f, sizeof dis);
  40. ++tim, q.push(node(, )), dis[] = ;
  41. for (node tmp; q.size(); )
  42. {
  43. tmp = q.top(), q.pop();
  44. if (dfn[tmp.x]==tim) continue;
  45. dfn[tmp.x] = tim;
  46. for (int i=head[tmp.x]; i!=-; i=nxt[i])
  47. if (tag[i]!=-)
  48. {
  49. int v = edges[i].v;
  50. if (dis[v] > dis[tmp.x]+edges[i].c)
  51. dis[v] = dis[tmp.x]+edges[i].c, q.push(node(v, dis[v]));
  52. }
  53. }
  54. for (int i=head[]; i!=-; i=nxt[i])
  55. if (tag[i]==-&&(ans==-||(ans > dis[edges[i].v]+edges[i^].c)))
  56. ans = dis[edges[i].v]+edges[i^].c;
  57. }
  58. int main()
  59. {
  60. memset(head, -, sizeof head);
  61. n = read(), m = read(), ans = edgeTot = -;
  62. for (int i=; i<=m; i++)
  63. {
  64. int u = read(), v = read(), s = read(), t = read();
  65. addedge(u, v, s), addedge(v, u, t);
  66. }
  67. for (int d=; d>=; --d)
  68. {
  69. for (int i=head[]; i!=-; i=nxt[i])
  70. if ((i>>d)&) tag[i] = , tag[i^] = -;
  71. else tag[i] = -, tag[i^] = ;
  72. dijkstra();
  73. for (int i=head[]; i!=-; i=nxt[i])
  74. if ((i>>d)&) tag[i] = -, tag[i^] = ;
  75. else tag[i] = , tag[i^] = -;
  76. dijkstra();
  77. }
  78. printf("%d\n",ans);
  79. return ;
  80. }

END

【技巧 二进制分组】bzoj4398: 福慧双修&&2407: 探险的更多相关文章

  1. [BZOJ4398]福慧双修/[BZOJ2407]探险

    题目大意: 给定一个$n(n\leq40000)$个点$m(m\leq100000)$条边的有向图,求从$1$出发回到$1$的不经过重复结点的最短路. 思路: 首先Dijkstra求出从1出发到每个结 ...

  2. 题解 bzoj 4398福慧双修(二进制分组)

    二进制分组,算个小技巧 bzoj 4398福慧双修 给一张图,同一条边不同方向权值不同,一条边只能走一次,求从1号点出发再回到1号点的最短路 一开始没注意一条边只能走一次这个限制,打了个从一号点相邻节 ...

  3. bzoj 4398 福慧双修——二进制分组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4398 如果枚举1号点走哪些点出去,就从那些点出发跑多源最短路即可.最短路不会重复经过一条边. ...

  4. 【Codeforces710F】String Set Queries (强制在线)AC自动机 + 二进制分组

    F. String Set Queries time limit per test:3 seconds memory limit per test:768 megabytes input:standa ...

  5. BZOJ2989 数列(二进制分组)

    这题其实可以cdq分治做,但是如果强制在线的话,这里有个牛逼方法叫二进制分组. 它的基本思想是把修改操作按二进制分组,遇到修改就在尾部加一个,并与之前的合并,比如之前有23(16+4+2+1)个,加了 ...

  6. 【BZOJ3821/UOJ46】玄学(二进制分组,线段树)

    [BZOJ3821/UOJ46]玄学(二进制分组,线段树) 题面 BZOJ UOJ 题解 呜,很好的题目啊QwQ. 离线做法大概可以线段树分治,或者直接点记录左右两次操作时的结果,两个除一下就可以直接 ...

  7. bzoj2989&&4170数列——二进制分组+主席树

    题意的转化挺巧妙的 可以联想到曼哈顿距离! 并且,所谓的修改还要查询历史版本,并且修改之间不动只算一次,不就是给平面上加一个点吗? 看成(x,a[x])的点 就是一个菱形区域 转切比雪夫距离,变成矩形 ...

  8. CodeForces - 710F:String Set Queries (二进制分组 处理 在线AC自动机)

    ou should process m queries over a set D of strings. Each query is one of three kinds: Add a string ...

  9. 2019.01.21 bzoj2989: 数列(二进制分组+主席树)

    传送门 二进制分组入门题. 主席树写错调题2h+2h+2h+体验极差. 题意简述:给一堆点,支持加入一个点,询问有多少个点跟(x,y)(x,y)(x,y)曼哈顿距离不超过kkk. 思路:题目要求的是对 ...

随机推荐

  1. flask LOCAL线程隔离技术

    from threading import Thread from werkzeug.local import Local local = Local()#实例化一个线程隔离对象 request = ...

  2. 多线程基础知识---join方法

    join方法的作用 thread.join()方法用于把指定的线程加入到当前线程中,把当前线程的CPU执行时间让给另一个线程.比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继 ...

  3. js放到head中失效的原因与解决方法

    1.今天写js碰到一个奇怪的问题,写好的js放到body里面执行,但是放到head中没有任何效果,为什么导致这种原因呢? 看失效代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 ...

  4. 菜鸟系列docker——搭建私有仓库harbor(6)

    docker 搭建私有仓库harbor 1. 准备条件 安装docker sudo yum update sudo yum install -y yum-utils device-mapper-per ...

  5. SQLite基础-6.运算符

    目录 SQLite 运算符 1. 运算符 2. 算数运算符 3. 比较运算符 4. 逻辑运算符 SQLite 运算符 1. 运算符 首先,问大家运算符是什么?运算符在很多领域均用使用.它也分很多中,常 ...

  6. centos 秘钥登录

    客户端系统:macOS 服务端系统:Centos7 另外:ip 为 172.25.11.182 用户名为 iamfine 1, 在客户端macOS上生成 rsa 对 ssh-keygen -t rsa ...

  7. Springboot导出Excel并下载

    引入相关依赖 <!--数据导出excel--> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> & ...

  8. O025、OpenStack 通用设计思路

    参考https://www.cnblogs.com/CloudMan6/p/5427981.html   API 前端服务   每个OpenStack组件可能包含若干子服务,其中必定有一个API服务负 ...

  9. Js-声明变量

    JS声明变量 js声明变量的方式有3种:let,const,var 1.const如果定义简单数据类型,变成常量,变量值不可以更改. const name="lili"; name ...

  10. .NET中跨线程访问winform控件的方法

    1 第一种方式 MethodInvoker invoker = () => { richTextBox1.AppendText(_ClientSocketModelConnectedEvent. ...