前言

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

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

题面

题目描述

在比特镇一共有\(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. Scratch少儿编程系列:(三)第一个例子 潜水员

    一. 选择背景 在上一节系统界面的介绍中,选择"6角色"的左边,点击"从背景库中选择背景". 选择主题水下中的"underwater3". ...

  2. DG on Windows 10 S: 执行任意代码

    DG on Windows 10 S: 执行任意代码 windows 10 S版本是什么鬼? 众所周知,我们使用的是windows 10企业版 LTSC.更准确一点,CMD运行winver,我的版本是 ...

  3. 【VS开发】C++调用外部程序

    关于三个SDK函数:WinExec, ShellExecute,CreateProcess的其他注意事项:[1]定义头文件必须定义以下两个头文件: [cpp] view plain copy #inc ...

  4. Windows.命令行(CMD)_执行命令&环境变量

    1.CMD命令中如果 命令有换行的话,就使用 ^来连接(这就类似于 Linux命令行中 \ 的作用) 2.环境变量 2.1.显示 所有环境变量的值,命令:set 2.2.显示 某个环境变量的值,命令 ...

  5. C语言I-博客作业04

    这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 C语言I博客作业04 我在这个课程的目标是 掌握使用for循环语句实现指定次数的循环程序设计 这个作业在那个具体方面帮助我实现目标 在编写 ...

  6. PostgreSQL dblink使用过程

    安装: 进入/root/postgresql-11.2/contrib/dblink make && make install 切换到postgres用户 [root@fce40690 ...

  7. 使用Object.create()实现继承 用 Object.create实现类式继承

    使用Object.create()实现继承:https://www.cnblogs.com/cuew1987/p/4075027.html 用 Object.create实现类式继承:https:// ...

  8. stdcall 函数调用过程(以delphi为例),还有负数的补码

    以delphi下调用stdcall 函数为例,从右往左压栈:procedure TForm1.Button2Click(Sender: TObject);var i:integer;begin i:= ...

  9. 计算机系统结构总结_Branch prediction

    Textbook:<计算机组成与设计——硬件/软件接口>  HI<计算机体系结构——量化研究方法>          QR Branch Prediction 对于下面的指令: ...

  10. mac安装卸载brew

    1.安装 访问https://brew.sh,copy图中的命令到命令行中,进行下载安装 2.卸载 官方版本的卸载: /usr/bin/ruby -e "$(curl -fsSL https ...