[bzoj4398] 福慧双修 最短路 二进制分组
题解:
考场上看的这道题,,,当时70分算法打挂了,今天才知道这个也是原题。。。。
首先,对于不跟1相邻的边,肯定不会经过两次,因为经过两次就回来了,除了增加路径长度之外没有任何意义。
但是跟1相邻的边是可能会经过2次的,因为虽然增加了路径长度,但这次回来就直接到终点了,所以完全可能重复走。
那么有一个很容易发现的思路是,我们可以强制某一条边是出边(即从1点出去可以走的边),其他边则为入边,这样的话就可以强制走不同的路了,但这样时间复杂度较大。考虑优化它。
可以发现,这样分组的本质就是要使得对于任意二元组(x, y)而言,x和y都至少要有两次被分配在不同的集合中,这样它们才可以互相搭配组成两条可能的路径。
为什么是两条呢?
对于相同的路径而言,分两个方向走一遍权值是不同的,也就是说对于这条路径:1 --- 2 --- 4 --- 3 --- 1,我既可以1 ---> 2 ---> 4 ---> 3 ---> 1,也可以1 <--- 2 <--- 4 <--- 3 <--- 1.如果只是单纯的把2,3两条边分在不同的集合当中,你根本不知道会找到哪条路径,也不知道是否这条路径刚好就是最优的那条。
观察到任意边的编号都是不同的,这意味这它们对应的二进制串至少有一位是不同的,所以我们可以枚举位数,按照当前位是0还是1给边分组,那么由于任意两个串对于的二进制串都至少有一位不同,因此它们一定会有一次被分在不同的集合当中。因为我们需要找到所有可能路径,所以要把当前位是0的分给s1和当前位是0的分给s2都试一遍才能保证正确性。
但实际上你会发现不用试2遍也可以过这道题,这是数据原因,,,因为我已经把我原来那份代码给hack掉了。。。。
因为你可以发现,会发生错误的几率是很低的,因为发生错误当且仅当对应的最短路径没有被找到,而这种情况出现在1号点对应的出边和入边的编号刚好所有不同的地方都是1 对 0或者0 对1,并且刚好那个1 对 0(0对1)就会将2条边分在错误的集合。
所以除非构造数据来卡,不然出现错误的可能性是很低的。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 50100
#define ac 401000 int n, m, t, ans = INT_MAX, k;
int dis[AC], s[ac], top;
int Head[AC], date[ac], Next[ac], len[ac], tot = ;
bool vis[AC]; struct road{
int x, y, dis1, dis2;
}way[ac]; struct node{
int dis, id;
}; struct cmp{
bool operator () (node a, node b)
{
return a.dis > b.dis;
}
};
priority_queue<node, vector<node>, cmp> q; inline int read()
{
int x = ;char c = getchar();bool z = false;
while(c > '' || c < '')
{
if(c == '-') z = true;
c = getchar();
}
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
if(!z) return x;
else return -x;
} inline void upmin(int &a, int b)
{
if(b < a) a = b;
} inline void upmax(int &a, int b)
{
if(b > a) a = b;
} inline void add(int f, int w, int S)
{
if(w == ) w = t;
date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
//printf("%d ---> %d : %d\n", f, w, S);
} void pre()
{
n = read(), m = read(), t = n + ;
for(R i = ; i <= m; i ++)
{
way[i].x = read(), way[i].y = read(), way[i].dis1 = read(), way[i].dis2 = read();
if(way[i].x == || way[i].y == ) s[++top] = i;
}
} void build(int x)
{
//printf("%d:\n", x);
int l = ;
tot = ;
memset(Head, , sizeof(Head));
for(R i = ; i <= m; i ++)
{
if(i == s[l])
{
if(k ^ (l & x))//不仅仅要被分在不同集合当中,
{//还需要x1s1 + x2s2 ; x1s2 + x2x1两种情况都出现一次才能包括所有的情况
if(way[i].x == ) add(way[i].x, way[i].y, way[i].dis1);
else add(way[i].y, way[i].x, way[i].dis2);
}
else
{
if(way[i].x == ) add(way[i].y, way[i].x, way[i].dis2);
else add(way[i].x, way[i].y, way[i].dis1);
}
l ++;
}
else
{
add(way[i].x, way[i].y, way[i].dis1);
add(way[i].y, way[i].x, way[i].dis2);
}
}
} void spfa()
{
int x, now;
memset(dis, , sizeof(dis));
memset(vis, , sizeof(vis));
dis[] = ;
q.push((node){, });
while(!q.empty())
{
x = q.top().id, q.pop();
while(vis[x] && !q.empty()) x = q.top().id, q.pop();
if(vis[x]) break;
vis[x] = true;
for(R i = Head[x]; i; i = Next[i])
{
now = date[i];
if(dis[now] > dis[x] + len[i])
{
dis[now] = dis[x] + len[i];
q.push((node){dis[now], now});
}
}
}
upmin(ans, dis[t]);
} void work()
{
int tmp = ;
for(R i = ; i <= ; i ++)
{
k = , build(tmp), spfa();
k = , build(tmp), spfa();
tmp <<= ;
}
printf("%d\n", ans);
} int main()
{
freopen("in.in", "r", stdin);
pre();
work();
fclose(stdin);
return ;
}
[bzoj4398] 福慧双修 最短路 二进制分组的更多相关文章
- HDU - 6166:Senior Pan(顶点集合最短路&二进制分组)
Senior Pan fails in his discrete math exam again. So he asks Master ZKC to give him graph theory pro ...
- hdu-6166(最短路+二进制分组)
题意:给你n个点m条边的有向图,然后再给你k个不同的点,问你这k个点的最小距离: 解题思路:这道题最需要注意的就是k个点一定是不同的,那么有一个结论就是任意两个不同的数字中,在他们的二进制地表示中,一 ...
- bzoj#2407-探险【最短路,二进制分组】
正题 题目链接:https://darkbzoj.tk/problem/2407 题目大意 \(n\)个点的一张无向图(但是正反权值不同),求一个从\(1\)出发回到\(1\)且不经过重复边的最短路径 ...
- 【技巧 二进制分组】bzoj4398: 福慧双修&&2407: 探险
二进制分组也可以说是一种比较优美的拆贡献方式吧? Description 菩萨为行,福慧双修,智人得果,不忘其本.——唐朠立<大慈恩寺三藏法师传>有才而知进退,福慧双修,这才难得.——乌雅 ...
- 题解 bzoj 4398福慧双修(二进制分组)
二进制分组,算个小技巧 bzoj 4398福慧双修 给一张图,同一条边不同方向权值不同,一条边只能走一次,求从1号点出发再回到1号点的最短路 一开始没注意一条边只能走一次这个限制,打了个从一号点相邻节 ...
- HDU 6166 Senior Pan(二进制分组+最短路)
题意 给出一个\(n\)个点\(m\)条边的有向图\((n,m<=100000)\),从中选择\(k\)个点\((k<=n)\),问这k个点两两之间的最短路最小值是多少? 思路 直接的想法 ...
- bzoj 4398 福慧双修——二进制分组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4398 如果枚举1号点走哪些点出去,就从那些点出发跑多源最短路即可.最短路不会重复经过一条边. ...
- HDU 6166 Senior Pan(多校第九场 二进制分组最短路)
题意:给出n个点和m条有向边(有向边!!!!我还以为是无向查了半天),然后给出K个点,问这k个点中最近的两点的距离 思路:比赛时以为有询问,就直接丢了,然后这题感觉思路很棒,加入把所有点分成起点和终点 ...
- 【Codeforces710F】String Set Queries (强制在线)AC自动机 + 二进制分组
F. String Set Queries time limit per test:3 seconds memory limit per test:768 megabytes input:standa ...
随机推荐
- ROS(一)Topic 通信
ROS系统起源于2007年斯坦福大学人工智能实验室的项目与机器人技术公司Willow Garage的个人机器人项目(Personal Robots Program)之间的合作,2008年之后就由Wil ...
- 通过反编译看Java String及intern内幕--费元星站长
通过反编译看Java String及intern内幕 一.字符串问题 字符串在我们平时的编码工作中其实用的非常多,并且用起来也比较简单,所以很少有人对其做特别深入的研究.倒是面试或者笔试的时候,往 ...
- iOS WKWebView添加进度条02
之前写了一个是关于webview添加进度条的,现在补一个WKWebView进度条. //添加一个全局属性 @property(nonatomic,strong)CALayer *progresslay ...
- uvaoj 101 - The Blocks Problem(vector应用+技巧)
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=835&page= ...
- 使用.net 更新word目录
方案一.采用OpenXml(服务器不依赖Office组件) 在word生成的最后加上代码: using (WordprocessingDocument docx = WordprocessingDoc ...
- lintcode 二叉树后序遍历
/** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * Tr ...
- HTML 之 表单
关于HTML的表单 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset=&q ...
- hibernate提示Unknown entity: :xxx
错误提示: org.hibernate.MappingException: Unknown entity: org.dao.po.Role at org.hibernate.internal.Sess ...
- 软工2017第四周作业结对编程——个人psp
29.22 --9.26本周例行报告 1.PSP(personal software process )个人软件过程. 类型 任务 预计时间 开始时间 结束时间 中断时间 ...
- Alpha冲刺——第一天
Alpha第一天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...