[HG]walk 题解
前言
学长博客划水,抄题解,差评。
于是我来重新写一篇正常的题解,虽然解法跟标程不一样,但是复杂度是一样的。
题面
题目描述
在比特镇一共有\(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 题解的更多相关文章
- Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解
Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解 题目地址:https://codingcompetitions.withgoogle.com/kickstar ...
- [HG]Market 题解
题目描述 在比特镇一共有 \(n\) 家商店,编号依次为 \(1\) 到 \(n\). 每家商店只会卖一种物品,其中第 \(i\) 家商店的物品单价为 \(c_i\),价值为 \(v_i\),且该商店 ...
- [HG]AK 题解
前言 什么鬼畜玩意,扶我起来,我要用__int128,这辈子都不珂能用龟速乘的... 真香. 题解 我们知道这个模数是个神奇的东西 \(2305843008676823040 = 2^{29} \ti ...
- CF1090M The Pleasant Walk 题解
Content 有一个长度为 \(n\) 的数组 \(a_1,a_2,a_3,...,a_n\),并已知有 \(k\) 个不相同的元素求最长连续的一段区间,使得这里面的元素互不相同. 数据范围:\(1 ...
- CF1119A Ilya and a Colorful Walk 题解
Content 有一个长度为 \(n\) 的数组 \(a_1,a_2,a_3,...,a_n\),试求出两个不相等的数之间的距离的最大值. 数据范围:\(3\leqslant n\leqslant 3 ...
- 18年10月30日 NOIP模拟赛
T1 jkl 题解 显然每次都取a[i]的最大值/最小值,并更新a[i]即可 用数据结构维护这一操作..得分看常数 事实上用v[i]记录权值为i的个数,然后for乱搞就可以了... 其它乱搞做法能获得 ...
- BZOJ3076 & 洛谷3081:[USACO2013 MAR]Hill Walk 山走——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=3076 https://www.luogu.org/problemnew/show/P3081#sub ...
- 题解报告:hdu 1142 A Walk Through the Forest
题目链接:acm.hdu.edu.cn/showproblem.php?pid=1142 Problem Description Jimmy experiences a lot of stress a ...
- [HG]子树问题 题解
前言 模拟赛赛时SubtaskR3没开long long丢了20分. 题意简述 题目描述 对于一棵有根树(设其节点数为 \(n\) ,则节点编号从 \(1\) 至 \(n\) ),如果它满足所有非根节 ...
随机推荐
- Scratch少儿编程系列:(三)第一个例子 潜水员
一. 选择背景 在上一节系统界面的介绍中,选择"6角色"的左边,点击"从背景库中选择背景". 选择主题水下中的"underwater3". ...
- DG on Windows 10 S: 执行任意代码
DG on Windows 10 S: 执行任意代码 windows 10 S版本是什么鬼? 众所周知,我们使用的是windows 10企业版 LTSC.更准确一点,CMD运行winver,我的版本是 ...
- 【VS开发】C++调用外部程序
关于三个SDK函数:WinExec, ShellExecute,CreateProcess的其他注意事项:[1]定义头文件必须定义以下两个头文件: [cpp] view plain copy #inc ...
- Windows.命令行(CMD)_执行命令&环境变量
1.CMD命令中如果 命令有换行的话,就使用 ^来连接(这就类似于 Linux命令行中 \ 的作用) 2.环境变量 2.1.显示 所有环境变量的值,命令:set 2.2.显示 某个环境变量的值,命令 ...
- C语言I-博客作业04
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 C语言I博客作业04 我在这个课程的目标是 掌握使用for循环语句实现指定次数的循环程序设计 这个作业在那个具体方面帮助我实现目标 在编写 ...
- PostgreSQL dblink使用过程
安装: 进入/root/postgresql-11.2/contrib/dblink make && make install 切换到postgres用户 [root@fce40690 ...
- 使用Object.create()实现继承 用 Object.create实现类式继承
使用Object.create()实现继承:https://www.cnblogs.com/cuew1987/p/4075027.html 用 Object.create实现类式继承:https:// ...
- stdcall 函数调用过程(以delphi为例),还有负数的补码
以delphi下调用stdcall 函数为例,从右往左压栈:procedure TForm1.Button2Click(Sender: TObject);var i:integer;begin i:= ...
- 计算机系统结构总结_Branch prediction
Textbook:<计算机组成与设计——硬件/软件接口> HI<计算机体系结构——量化研究方法> QR Branch Prediction 对于下面的指令: ...
- mac安装卸载brew
1.安装 访问https://brew.sh,copy图中的命令到命令行中,进行下载安装 2.卸载 官方版本的卸载: /usr/bin/ruby -e "$(curl -fsSL https ...