前言

学长博客划水,抄题解,差评。

于是我来重新写一篇正常的题解,虽然解法跟标程不一样,但是复杂度是一样的。

题面

题目描述

在比特镇一共有\(n\)个街区,编号依次为\(1\)到\(n\),它们之间通过若干条单向道路连接。

比特镇的交通系统极具特色,除了\(m\)条单向道路之外,每个街区还有一个编码\(val_i\),

不同街区可能拥有相同的编码。如果\(val_i\ and\ val_j = val_j\)(博主注:and这里指位与,即C++中的&),

即\(val_i\)在二进制下与\(val_j\)做与运算等于\(val_j\),那么也会存在一条额外的从\(i\)出发到\(j\)的单向道路。

\(Byteasar\)现在位于\(1\)号街区,他想知道通过这些道路到达每一个街区最少需要多少时间。

因为比特镇的交通十分发达,你可以认为通过每条道路都只需要\(1\)单位时间。

输入

第一行包含两个正整数\(n,m\),表示街区的总数以及道路的总数。

第二行包含\(n\)个正整数\(val_1,val_2,\dots,val_n\),分别表示每个街区的编码。

接下来\(m\)行,每行包含两个正整数\(u_i,v_i\),表示一条单向道路,起点为\(u_i\),终点为\(v_i\)。

输出

输出\(n\)行,每行一个整数,其中第\(i\)行输出到达第\(i\)个街区的最少时间,如果无法到达则输出\(-1\)。

题解

这是一道好题,主要难点在于建图,

一开始我想到暴力连边,然后跑dijkstra。

像这样(注意,下面的代码是会TLE的):

#include <cstdio>
#include <set> using namespace std; int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
} struct Edge{
int to, next;
} edges[600005]; int head[200005], edge_num; void addEdge(int from, int to){
edges[++edge_num] = (Edge){to, head[from]};
head[from] = edge_num;
} long long dist[200005];
int val[200005]; set< pair<long long, int> > que;
int n, m; void dijkstra(int st){
que.clear();
for (int i = 1; i <= n; ++i)
dist[i] = -1;
dist[st] = 0;
pair<long long, int> cur; cur.first = 0, cur.second = st;
que.insert(cur); int u, v;
while (!que.empty()){
u = (*que.begin()).second; que.erase(que.begin());
for (int c_e = head[u]; c_e; c_e = edges[c_e].next){
v = edges[c_e].to;
if (dist[v] == -1 || dist[u] + 1 < dist[v]){
que.erase(make_pair(dist[v], v));
dist[v] = dist[u] + 1;
que.insert(make_pair(dist[v], v));
}
}
}
} int main(){
freopen("walk.in", "r", stdin);
freopen("walk.out", "w", stdout);
n = read(), m = read();
for (int i = 1; i <= n; ++i) val[i] = read();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if ((val[i] & val[j]) == val[j])
addEdge(i, j);
for (int i = 1; i <= m; ++i){
int u = read(), v = read();
addEdge(u, v);
}
dijkstra(1);
for (int i = 1; i <= n; ++i)
printf("%lld\n", dist[i]);
fclose(stdin);
fclose(stdout);
return 0;
}

然后突然发现,边权为1,为什么要跑dijkstra,QAQ。

于是换成了一个BFS。(博主太蠢了...)

但是建边还是会TLE。

于是我们想到,如果把每个点的val也当做一个点,然后将每个点练到val,权为0;每个val连会点,权为1。

然后接着在val之间建边权为0的边即可。

但是val之间的边要是预先处理,那么铁定MLE(边太多了)。

但是我们发现,每个点肯定只会访问一次,那么不如在BFS的时候枚举找到能够去的val点。

然后我们进一步发现如果每次val点搜到所有它能去的点,那么肯TLE,但是我们发现,由于权为0,不如只连在二进制差一位的点,即可(由只差一位的点继续去更新差两、三...位的点)。

然后就解决了,还有一个问题就是有的边权为0,有的边权为1,普通的BFS貌似会崩,但是这种边权我们一看就知道需要用到双端队列BFS。

如果是权为0的边,那么把到达的点插入队首,否则插入队尾,正确性显然,仍然符合BFS的更新规则。

代码

#include <cstdio>
#include <map>
#include <queue> using namespace std; const int MAXN = (1 << 20); int read(){
int x = 0; int zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
} struct Edge{
int to, next; bool val;
} edges[5000005]; int head[2000005], edge_num;
int val[2000005];
long long dist[2000005]; void addEdge(int from, int to, bool val){
edges[++edge_num] = (Edge){to, head[from], val};
head[from] = edge_num;
} deque< pair<int, int> > que;
int vis[2000005]; void BFS(){
dist[1 + MAXN] = 0;
que.clear();
que.push_back(make_pair(1 + MAXN, 0)); vis[1 + MAXN] = 1;
int u, v;
while (!que.empty()){
u = que.front().first, dist[u] = que.front().second, que.pop_front();
if (u <= MAXN){
for (int i = 0; i <= 20; ++i)
if (((1 << i) & u) && !vis[u ^ (1 << i)])
que.push_front(make_pair(u ^ (1 << i), dist[u])), vis[u ^ (1 << i)] = 1;
if (!vis[0])
que.push_front(make_pair(0, dist[u])), vis[0] = 1;
}
for (int c_e = head[u]; c_e; c_e = edges[c_e].next){
v = edges[c_e].to;
if (!vis[v]){
vis[v] = 1;
if (!edges[c_e].val)
que.push_front(make_pair(v, dist[u]));
else
que.push_back(make_pair(v, dist[u] + 1));
}
}
}
} int main(){
int n = read(), m = read();
for (int i = 1; i <= n; ++i){
val[i] = read();
addEdge(i + MAXN, val[i], 0), addEdge(val[i], i + MAXN, 1);
}
for (int i = 1; i <= m; ++i){
int u = read() + MAXN, v = read() + MAXN;
addEdge(u, v, 1);
}
for (int i = 0; i <= MAXN + n; ++i)
dist[i] = -1;
BFS();
for (int i = MAXN + 1; i <= MAXN + n; ++i)
printf("%lld\n", dist[i]);
return 0;
}

后记

博主很菜,也没怎么卡常,但是效率是十分优秀的。

如果您想了解更多解法(BFS+DFS枚举子集(标算打法),dijkstra)请去Chhokmah的博客

[HG]walk 题解的更多相关文章

  1. Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解

    Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解 题目地址:https://codingcompetitions.withgoogle.com/kickstar ...

  2. [HG]Market 题解

    题目描述 在比特镇一共有 \(n\) 家商店,编号依次为 \(1\) 到 \(n\). 每家商店只会卖一种物品,其中第 \(i\) 家商店的物品单价为 \(c_i\),价值为 \(v_i\),且该商店 ...

  3. [HG]AK 题解

    前言 什么鬼畜玩意,扶我起来,我要用__int128,这辈子都不珂能用龟速乘的... 真香. 题解 我们知道这个模数是个神奇的东西 \(2305843008676823040 = 2^{29} \ti ...

  4. CF1090M The Pleasant Walk 题解

    Content 有一个长度为 \(n\) 的数组 \(a_1,a_2,a_3,...,a_n\),并已知有 \(k\) 个不相同的元素求最长连续的一段区间,使得这里面的元素互不相同. 数据范围:\(1 ...

  5. CF1119A Ilya and a Colorful Walk 题解

    Content 有一个长度为 \(n\) 的数组 \(a_1,a_2,a_3,...,a_n\),试求出两个不相等的数之间的距离的最大值. 数据范围:\(3\leqslant n\leqslant 3 ...

  6. 18年10月30日 NOIP模拟赛

    T1 jkl 题解 显然每次都取a[i]的最大值/最小值,并更新a[i]即可 用数据结构维护这一操作..得分看常数 事实上用v[i]记录权值为i的个数,然后for乱搞就可以了... 其它乱搞做法能获得 ...

  7. BZOJ3076 & 洛谷3081:[USACO2013 MAR]Hill Walk 山走——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3076 https://www.luogu.org/problemnew/show/P3081#sub ...

  8. 题解报告:hdu 1142 A Walk Through the Forest

    题目链接:acm.hdu.edu.cn/showproblem.php?pid=1142 Problem Description Jimmy experiences a lot of stress a ...

  9. [HG]子树问题 题解

    前言 模拟赛赛时SubtaskR3没开long long丢了20分. 题意简述 题目描述 对于一棵有根树(设其节点数为 \(n\) ,则节点编号从 \(1\) 至 \(n\) ),如果它满足所有非根节 ...

随机推荐

  1. 【ABAP系列】SAP Web Dynpro 技术简介

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP Web Dynpro 技 ...

  2. Linux setup

    Install centos 7: Config Network a config example in /etc/sysconfig/network-scripts/ifcfg-ens160 TYP ...

  3. 【Qt开发】事件循环与线程 二

    事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...

  4. 【linux开发】apt源设置

    不同的网络状况连接以下源的速度不同, 建议在添加前手动验证以下源的连接速度(ping下就行),选择最快的源可以节省大批下载时间. 首先备份源列表: sudo cp /etc/apt/sources.l ...

  5. 关于MySQL的安装使用心得

    MySQL浅浅地学习了几天,当然还是转到正轨Java上来了,昨天打了一串代码,测试注解来着,结果MySQL挂了~~~ 如何干净卸载MySQL帖子有很多,不再赘述,注册表是个好东西~~ 卸载了Mysql ...

  6. realloc ------ 扩大malloc得到的内存空间

    char* p = malloc(1024);char* q = realloc(p,2048); 现在的问题是我们应该如何处理指针 p. 刚开始按照我最直观的理解,如果就是直接将 p = NULL; ...

  7. Vue 实现手动刷新组件

    Vue 实现手动刷新组件:https://www.jianshu.com/p/742142dc95f3

  8. Nginx 3.使用配置

    转 https://www.cnblogs.com/wcwnina/p/9946747.html 本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文 ...

  9. 安装libpng库

    一. 安装libpng库前需要先安装zlib库,libpng库依赖zlib库 1.1. zlib库安装 1.1.1. 下载地址:http://www.zlib.net/ 1.1.2. 解压后得到zli ...

  10. H264 RTP包解析

    1.  预备 视频: 由一副副连续的图像构成,由于数据量比较大,因此为了节省带宽以及存储,就需要进行必要的压缩与解压缩,也就是编解码. h264裸码流: 对一个图像或者一个视频序列进行压缩,即产生码流 ...