[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\) ),如果它满足所有非根节 ...
随机推荐
- is_displayed()检查元素是否可见
返回的结果是bool类型,以百度首页为案例,来验证"©2019 Baidu 使用百度前必读意见反馈京ICP证030173号 "是否可见,见实现的代码: from selenium ...
- LSTM用于MNIST手写数字图片分类
按照惯例,先放代码: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #载入数据集 ...
- k8s/01开启云原生之门(Mooc)
一.kubernetes(k8s)基础知识 1.简介 在2017年Kubernetes战胜了两个强大的竞争对手Swarm和Mesos,成为容器管理与调度编排领域的首选平台和事实标准. 2014年k8s ...
- python+selenium模拟键盘输入
from selenium.webdriver.common.keys import Keys #键盘导入类 --------------------------------------------- ...
- Java基础语法—流程控制语句
在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的.所以,我们必须清楚每条语句的执行流程.而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能. 3.1 流程控制语句分类 ...
- 2019 CSP-S初赛游记
2019-10-19 ——这个注定要被载入史册的日子 作为一名初中生,和lpy大佬一同参加提高组的比赛,而今年普及组和提高组的时间竟然不一样,于是——凌晨六点半,来到了pdyz和高中生一起坐车去. 高 ...
- gRPC go安装教程
安装protobuf go get -u github.com/golang/protobuf/proto go get -u github.com/golang/protobuf/protoc-ge ...
- div的edit和drag(点击div可编辑、删除、拖动)
1.可编辑: <div id="move" contentEditable="true">可编辑</div> 设置contentEdit ...
- Docker 启动与停止容器
启动已运行过的容器 docker start 容器名称|容器id 如: docker start mycentos 启动所有运行过的容器(注意:反单引号` `), docker ps -a -q 是查 ...
- 关于Mysql 修改密码的记录
初次安装后完毕,使用管理员身份进入cmd界面, 输入" mysql -u root -p",出现"Enter password:",直接回车输入" s ...