牛客多校H题题解
链接:[https://ac.nowcoder.com/acm/contest/81597/H]
来源:牛客网
题目描述
Red stands at the coordinate \((0,0)\) of the Cartesian coordinate system. She has a string of instructions: up, down, left, right (where 'right' increases the x-coordinate by \(1\), and 'up' increases the y-coordinate by \(1\)).Now Red wants to select a continuous substring of instructions and execute them. Red hopes that the final execution of the instructions can pass through the coordinate \((x,y)\). She wants to know how many selection options there are.
输入描述:
The first line contains three integers \(n\), \(x\), and \(y\) ( \(1≤n≤2×10^5\),\(−10^5≤,≤10^5\))(\(1≤n≤2×10^5\),\(−10^5≤x,y≤10^ 5\)), —the length of the instruction string and the coordinates Red hopes to pass through.
The second line contains a string of length n, consisting of the characters '\(W\)', '\(S\)', '\(A\)', and '\(D\)'. — the four directions: up, down, left, and right, respectively.
输出描述:
Output one integer representing the number of selection options for the continuous substring.
解题大概思路
该题目我们想的暴力思路大概就是通过构建两个前缀和数组来,每次通过哈希表查找前缀和与目标\(x\) (或者\(y\))的对应的前缀和是否存在,如果存在,就把该区间存入区间另外一个哈希表内.这一部分的时间复杂度为\(O(nlog(n))\)级别
然后跑了一遍\(O(n)\)循环之后,再遍历一遍存储着符合条件的区间的哈希表,从哈希表里面选出符合要求的区间,这里特别要注意统计区间的时候要考虑清楚,避免重复统计区间
代码处理:
特殊判定 起点为(0,0)的情况下:
if(!x && !y) {
cout << n * (n + 1) / 2 << nn;
return;
}
初始化哈希表,记录出现过的区间
mp[{0, 0}].push_back(0);
set<pair<int, int>> se;
初始化前缀和数组
sx[i] = sx[i - 1] + dx[s[i]];
sy[i] = sy[i - 1] + dy[s[i]];
每次插入一个二维前缀和的时候,通过\(map\) 用\(O(log(n))\) 的时间复杂度来进行查询是否查找到了所需要的区间
像我比赛时写的代码进行了三次哈希查找,其中有一个哈希查找还相互嵌套,时间复杂度大概是\(O((log(n))^2)\)所以TLE了
auto v = mp[{sx[i] - x, sy[i] - y}];
//取出 右边的下标
if(!v.empty()) {
//如果找到了对应的一个前缀和
for(auto l: v) {
// if(l == i) se.insert({l, i});
if(l + 1 <= i) se.insert({l + 1, i});
//前面的if判断其实很多余
}
}
最后再在大的set里面找出所需要的区间
其实这里就算不用位图去重也可以的,因为此时哈希表里面的区间都是满足题目意思的区间
所以这里我感觉直接输出
se.size()就是最佳答案
for(auto x: se) {
int l = x.first, r = x.second;
if(vis[l]) continue;
vis[l] = 1;
ans += n - r + 1;
//如果有效就加上 n-r+1
}
AC code:
#include<bits/stdc++.h>
#define int long long
#define nn '\n'
using namespace std;
const int maxn = 2e5 + 5;
int sx[maxn], sy[maxn]; // 位置从1开始
bitset<maxn> vis;
void solve() {
int n, x, y, ans = 0;
string s;
cin >> n >> x >> y >> s;
if(!x && !y) {
cout << n * (n + 1) / 2 << nn;
return;
}
//特殊判定 起点为(0,0)的情况下
s = '0' + s;
map<char, int> dx, dy;
dx['D'] = 1, dx['A'] = -1;
dy['W'] = 1, dy['S'] = -1;
map<pair<int, int>, vector<int>> mp;
//初始化哈希表,记录出现过的区间
mp[{0, 0}].push_back(0);
set<pair<int, int>> se;
for(int i = 1; i <= n; i++) {
sx[i] = sx[i - 1] + dx[s[i]];
sy[i] = sy[i - 1] + dy[s[i]];
//初始化前缀和数组
mp[{sx[i], sy[i]}].push_back(i);
//直接这样插入元素???
auto v = mp[{sx[i] - x, sy[i] - y}];
//取出 右边的下标
if(!v.empty()) {
//如果找到了对应的一个前缀和
for(auto l: v) {
// if(l == i) se.insert({l, i});
if(l + 1 <= i) se.insert({l + 1, i});
//前面的if判断其实很多余
}
}
}
// for(auto x: se) {
// cout << x.first << ' ' << x.second << nn;
// }
set<string> ss;
//然后现在要对存入区间内的有效区间0进行判断
for(auto x: se) {
int l = x.first, r = x.second;
if(vis[l]) continue;
vis[l] = 1;
ans += n - r + 1;
//如果有效就加上 n-r+1
}
cout << ans << nn;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _ = 1;
// cin >> _;
while(_--)
solve();
return 0;
}
这里附上一篇大佬的代码,简洁优雅简直让人拜服!
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a, b, ans;
char c;
pair<int, int> m[1000005];
map<pair<int, int>, int> t;
signed main()
{
cin >> n >> a >> b;
if (a == 0 && b == 0) {
cout << n * (n + 1) / 2;
return 0;
}
for (int i = 1; i <= n; i++) {
cin >> c;
if (c == 'A') m[i] = {m[i - 1].first - 1, m[i - 1].second + 0};
if (c == 'S') m[i] = {m[i - 1].first + 0, m[i - 1].second - 1};
if (c == 'W') m[i] = {m[i - 1].first + 0, m[i - 1].second + 1};
if (c == 'D') m[i] = {m[i - 1].first + 1, m[i - 1].second + 0};
}
for (int i = n; i > 0; i--) {
t[{m[i].first - a, m[i].second - b}] = n - i + 1;
ans += t[{m[i - 1].first, m[i - 1].second}];
}
cout <<br ans;
return 0;
}
那么这段AC代码到底包含着什么样的逻辑呢??
1.首先通过设置前缀和来统计各个前缀字符串到达的位置
2.反向进行遍历,如果此时map里面存在元素,那么map内包含的也就是n-i+1(后面计算过的),如果不存在,就是0;
那为什么要反向排列呢?因为反向排列是从后面的元素开始逆推,如果是正向开始,貌似也行哦,只需要重复该步骤就可以了,这里的处理是真的优雅!!!!
牛客多校H题题解的更多相关文章
- 数位dp——牛客多校H
/* x[1,A] y[1,B] x^y<C 或 x&y>C 把ABC拆成二进制后按位进行数位dp dp[pos][s1][s2][f1][f2] 表示从高到低第pos位,条件一状 ...
- LGV算法 CodeForces 348D + 牛客多校 A Monotonic Matrix
定理(Lindström–Gessel–Viennot lemma)很简单: 学的时候忘了大的行列式怎么算的了.. 然后就可以写题了: 第一道:CodeForces-348D(链接https://vj ...
- 2020牛客多校第八场K题
__int128(例题:2020牛客多校第八场K题) 题意: 有n道菜,第i道菜的利润为\(a_i\),且有\(b_i\)盘.你要按照下列要求给顾客上菜. 1.每位顾客至少有一道菜 2.给顾客上菜时, ...
- 2019牛客多校第八场 F题 Flowers 计算几何+线段树
2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...
- 2019年牛客多校第一场B题Integration 数学
2019年牛客多校第一场B题 Integration 题意 给出一个公式,求值 思路 明显的化简公式题,公式是分母连乘形式,这个时候要想到拆分,那如何拆分母呢,自然是裂项,此时有很多项裂项,我们不妨从 ...
- 2019牛客多校 Round4
Solved:3 Rank:331 B xor 题意:5e4个集合 每个集合最多32个数 5e4个询问 询问l到r个集合是不是都有一个子集的xor和等于x 题解:在牛客多校第一场学了线性基 然后这个题 ...
- 牛客多校第3场 J 思维+树状数组+二分
牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...
- 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响
不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号: 大数据躺过的坑 Java从入门到架构师 人工智能躺过的坑 ...
- 2019牛客多校第一场 I Points Division(动态规划+线段树)
2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...
- 牛客多校第一场 B Inergratiion
牛客多校第一场 B Inergratiion 传送门:https://ac.nowcoder.com/acm/contest/881/B 题意: 给你一个 [求值为多少 题解: 根据线代的知识 我们可 ...
随机推荐
- Camera | 1.Camera基础知识
一口君最近在玩瑞芯微的板子,之前写了几篇基于瑞芯微的文章,大家可以学习一下. <瑞芯微rk356x板子快速上手> <Linux驱动|rtc-hym8563移植笔记> <L ...
- js正则匹配以$开头和结尾的内容,并改变颜色
let res = "$你好你好$" res = res.replace(/\$(?<=\$).*?(?=\$)\$/g, `<span onclick="( ...
- 2022 CCPC 广州站 Alice and Her Lost Cat
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define rg register 4 #define ll long long ...
- 神经网络之卷积篇:详解池化层(Pooling layers)
详解池化层 除了卷积层,卷积网络也经常使用池化层来缩减模型的大小,提高计算速度,同时提高所提取特征的鲁棒性,来看一下. 先举一个池化层的例子,然后再讨论池化层的必要性.假如输入是一个4×4矩阵,用到的 ...
- sicp每日一题[2.13-2.16]
Exercise 2.13 Show that under the assumption of small percentage tolerances there is a simple formul ...
- docker 安装启动jenkins 以及问题剖析
docker 安装启动jenkins 以及问题剖析 首先,你环境必须要有docker,我这里是自己本地虚拟机Vmware,我的虚拟机时linux centos7的 .如果你不知怎么安装虚拟机和命令 ...
- 大一下的acm生活
在一个名气不大的211学校刷题的日常. 感觉这些算法题好难啊! 最近有好多实验室要招新,不知道该怎么办,自己只想就业,并不想升学,好烦! 真枯燥,好无聊. 现在要学习相关的网页设计和网站建设,例如配色 ...
- IDEA如何自动导入依赖的jar包
前言 我们在使用IDEA开发时,会引入第三方的jar包,这些第三方的jar包使我们可以快速的使用别人开发好的功能,而不用重复造轮子了. 这大大提高了我们的开发效率. 但是,有时候我们一下子需要导入太多 ...
- Docker安装(安装Docker-CE)(三)
现版本安装Docker已经非常简单了,有很多种方式,而自17年开始,Docker分为Docker-CE(社区版).Docker-EE(企业版),另外Docker-IO是较早的版本,通常用的都是Dock ...
- C++指针等于地址加偏移量
概述 本文通过c++示例代码演示指针的加减法运算及对 "指针 = 地址 + 偏移量" 的理解. 研究示例 1. 首先来检查各种变量类型所占的内存大小 #include <io ...