链接:[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题题解的更多相关文章

  1. 数位dp——牛客多校H

    /* x[1,A] y[1,B] x^y<C 或 x&y>C 把ABC拆成二进制后按位进行数位dp dp[pos][s1][s2][f1][f2] 表示从高到低第pos位,条件一状 ...

  2. LGV算法 CodeForces 348D + 牛客多校 A Monotonic Matrix

    定理(Lindström–Gessel–Viennot lemma)很简单: 学的时候忘了大的行列式怎么算的了.. 然后就可以写题了: 第一道:CodeForces-348D(链接https://vj ...

  3. 2020牛客多校第八场K题

    __int128(例题:2020牛客多校第八场K题) 题意: 有n道菜,第i道菜的利润为\(a_i\),且有\(b_i\)盘.你要按照下列要求给顾客上菜. 1.每位顾客至少有一道菜 2.给顾客上菜时, ...

  4. 2019牛客多校第八场 F题 Flowers 计算几何+线段树

    2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...

  5. 2019年牛客多校第一场B题Integration 数学

    2019年牛客多校第一场B题 Integration 题意 给出一个公式,求值 思路 明显的化简公式题,公式是分母连乘形式,这个时候要想到拆分,那如何拆分母呢,自然是裂项,此时有很多项裂项,我们不妨从 ...

  6. 2019牛客多校 Round4

    Solved:3 Rank:331 B xor 题意:5e4个集合 每个集合最多32个数 5e4个询问 询问l到r个集合是不是都有一个子集的xor和等于x 题解:在牛客多校第一场学了线性基 然后这个题 ...

  7. 牛客多校第3场 J 思维+树状数组+二分

    牛客多校第3场 J 思维+树状数组+二分 传送门:https://ac.nowcoder.com/acm/contest/883/J 题意: 给你q个询问,和一个队列容量f 询问有两种操作: 0.访问 ...

  8. 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  9. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  10. 牛客多校第一场 B Inergratiion

    牛客多校第一场 B Inergratiion 传送门:https://ac.nowcoder.com/acm/contest/881/B 题意: 给你一个 [求值为多少 题解: 根据线代的知识 我们可 ...

随机推荐

  1. 从0实现基于Linux socket聊天室-多线程服务器模型-1

    前言 Socket在实际系统程序开发当中,应用非常广泛,也非常重要.实际应用中服务器经常需要支持多个客户端连接,实现高并发服务器模型显得尤为重要.高并发服务器从简单的循环服务器模型处理少量网络并发请求 ...

  2. 【CMake系列】03-cmake 注释、常用指令 message、set、file、for_each、流程控制if

    本文给出了 cmake 中的 一些常用的 指令,可以快速了解,为后面的内容深入 打点基础. 本专栏的详细实践代码全部放在 github 上,欢迎 star !!! 如有问题,欢迎留言.或加群[3927 ...

  3. .NETCore 服务的三种生命周期

    一.接口定义 public interface ITestSerivceSingleton { public string GetServiceNameBase() { return "IT ...

  4. win10无法访问共享文件夹win2008R2 错误代码0X80004005

    错误代码0X80004005 无法访问共享计算机的解决方法 开始->运行(快捷键"win+R"),输入"regedit"后回车,打开注册表编辑器. 依次打 ...

  5. 组合数取模的几种方法--Exlucas&杨辉三角&组合

    组合数取模的几个方法 求: \[C^{m}_{n} \bmod P \] 1.杨辉三角法 \[C^{m}_{n} = C^{m - 1}_{n - 1} + C^{ m }_{n - 1} \] 时间 ...

  6. python的命名风格(下划线篇)

    一个下划线开头的代表模块私有 用from xxx import * 时python会自动屏蔽带下划线的东西,想要取消屏蔽可以用__all__方法,但不建议(不符合规范) 两个下划线开头的代表类私有

  7. Feign 动态设定服务器名称 与 调用接口

    1. 新增编码器(由于使用了动态的Feign,所以不能像正常使用Feign一样指定configuration配置编码器) import feign.RequestTemplate; import fe ...

  8. Git使用经验总结6-删除远端历史记录

    删除远端的历史记录但是不影响最新的仓库内容是笔者一直想实现的功能,有两个很不错的用处: 有的历史提交不慎包含了比较敏感的信息,提交的时候没注意,过了一段时间才发现.这个时候已经有了很多新的历史提交,无 ...

  9. 原生JavaScript实现可旋转立方体效果基础示例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 人脸伪造图像检测:Deepfake魔高一尺,TextIn道高一丈

      只因开了一个视频会议,直接被骗1.8个亿 今年2月,一家跨国公司的香港分公司财务人员被一场精心策划的Deepfake视频会议诈骗,导致公司损失2亿港币(约1.8亿人民币). 事件起因是财务人员收到 ...