~~~题面~~~

题解:

  考场上看的这道题,,,当时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] 福慧双修 最短路 二进制分组的更多相关文章

  1. HDU - 6166:Senior Pan(顶点集合最短路&二进制分组)

    Senior Pan fails in his discrete math exam again. So he asks Master ZKC to give him graph theory pro ...

  2. hdu-6166(最短路+二进制分组)

    题意:给你n个点m条边的有向图,然后再给你k个不同的点,问你这k个点的最小距离: 解题思路:这道题最需要注意的就是k个点一定是不同的,那么有一个结论就是任意两个不同的数字中,在他们的二进制地表示中,一 ...

  3. bzoj#2407-探险【最短路,二进制分组】

    正题 题目链接:https://darkbzoj.tk/problem/2407 题目大意 \(n\)个点的一张无向图(但是正反权值不同),求一个从\(1\)出发回到\(1\)且不经过重复边的最短路径 ...

  4. 【技巧 二进制分组】bzoj4398: 福慧双修&&2407: 探险

    二进制分组也可以说是一种比较优美的拆贡献方式吧? Description 菩萨为行,福慧双修,智人得果,不忘其本.——唐朠立<大慈恩寺三藏法师传>有才而知进退,福慧双修,这才难得.——乌雅 ...

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

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

  6. HDU 6166 Senior Pan(二进制分组+最短路)

    题意 给出一个\(n\)个点\(m\)条边的有向图\((n,m<=100000)\),从中选择\(k\)个点\((k<=n)\),问这k个点两两之间的最短路最小值是多少? 思路 直接的想法 ...

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

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

  8. HDU 6166 Senior Pan(多校第九场 二进制分组最短路)

    题意:给出n个点和m条有向边(有向边!!!!我还以为是无向查了半天),然后给出K个点,问这k个点中最近的两点的距离 思路:比赛时以为有询问,就直接丢了,然后这题感觉思路很棒,加入把所有点分成起点和终点 ...

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

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

随机推荐

  1. java nio之channel

    一.通道(Channel):由 java.nio.channels 包定义的.Channel 表示 IO 源与目标打开的连接.Channel 类似于传统的“流”.只不过 Channel本身不能直接访问 ...

  2. LWM2M的DISCOVER操作

    1. 先看下DISCOVER的数据流,工作服务器下发的指令到设备客户端 2. 解释,这个操作是用来发现Object, Object Instances, and Resources的属性,同时可以发现 ...

  3. SpringBoot学习:添加JSP支持

    项目下载地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)pom中添加依赖: <!-- https://mvnrepository.c ...

  4. Awesome Django

     Awesome Django    If you find Awesome Django useful, please consider donating to help maintain it. ...

  5. C#原型模式

    如下: [Serializable] public class ModelNewTable : ICloneable { public object Clone() { using (var stre ...

  6. 生鲜水果商城PC手机微信完整版源码2018版(免费)

    采用php+mysql架构,含有PC.手机.微信三端,只需要修改一下数据库配置,并恢复一下数据即可使用,还有微信.支付宝等接口,如有问题请在文章下面留言一下,我看到会协助一下的,下载包里面含有详细的安 ...

  7. (C#)工厂方法模式

    1.工厂方法模式 第一了一个用于创建对象的接口,让子类自己决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. *工厂方法模式即克服了简单工厂模式违反开放-封闭原则的缺点,又保留了封装对象创建过 ...

  8. 一段代码-Java

    在打算写这么一篇文章的时候,想到很多,觉得要是全都写下来的话,估计BZ也不知道要写多少,总之,好多吧!那么,就让BZ一切从简... 我们知道java它的特殊性在于,用它所写代码的运行是依靠自己的一套j ...

  9. C# 中访问修饰符

    1.public 完全公开的,公共的 2. private 私有的,只能在当前类的内部访问, 不可修饰类 3.protected 受保护的,只能在当前类的内部以及其子类中访问,不能用来修饰类 4.in ...

  10. MySQL数据库怎么截取字符串?

    函数: 1.从左开始截取字符串 left(str, length) 说明:left(被截取字段,截取长度) 例:select left(content,200) as abstract from my ...