前言

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

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

题面

题目描述

在比特镇一共有\(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. idea中dbug模式的使用

    1:进入断点状态时候每个按钮的用途如图: 2:当运行过得代码需要查看输出内容时候,可以选中需要查看的代码进行运行如图:会在控制台输出选中代码执行的结果

  2. 11.metasploit辅助模块----基本Exp----ARP欺骗中间人MITM----WordPress破解

    metasploit辅助模块 信息收集 auxiliary scanners 使用metasploitable靶机 桥接 同一局域网 msfconsole nmap -sT 靶机IP nmap -sS ...

  3. spark MLlib矩阵四则运算,线性代数

    1.导包请看我的上一篇博文,maven项目的包 https://www.cnblogs.com/wuzaipei/p/10965680.html 2.denseMatirx 矩阵四则运算如下 版本不同 ...

  4. eclipse sts 快捷键

    1. ctrl + m 全屏/取消全屏 2.快速切换上面标签 ctrl + pageup/pagedown   : 标签左右切换 ctrl + < > : 标签前后访问处切换 New Ja ...

  5. /proc/cpuinfo 查看cpu信息

    /proc/cpuinfo 查看cpu信息 如类型.厂家.型号

  6. adb,aapt等命令使用

    adb         install/uninstall:安装/卸载手机中的应用.         devices:查看当前连接到电脑中的设备. adb shell         首先运行adb ...

  7. PHP 经典有趣的算法

    原文:https://blog.csdn.net/a519395243/article/details/77942913 1.一群猴子排成一圈,按1,2,…,n依次编号.然后从第1只开始数,数到第m只 ...

  8. 搜索专题: HDU1026Ignatius and the Princess I

    Ignatius and the Princess I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  9. 连连看(简单搜索)bfs

    连连看Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  10. C语言中整形数组、字符数组、字符串的区别

    一. 第一 整型数组的存放,数组最后是不加'\0'的,字符串会自动加上,因此存放字符的时候数组的大小要比实际字符的多一个 第二 整型数组 每一个单元是4个字节的,字符串是一个一个字符存放的,每个字符占 ...