codeforces基础题——#361(div2)D
2016-09-08 20:15
429 查看
#361(Div 2) D
题目大意 :给你a, b两个长度为n的整数序列(1 <= n <= 200000), 问有多少个(l, r)(1 <= l, r <= n )使得a中区间(l, r)的最大值与b中区间(l, r)的最小值一样 题解 : 一开始在想神奇的树状数组瞎搞做法, 其实可以做, 但写起来很麻烦, 于是写到一半太累(懒)了, 就没再写…… 此题正解十分简洁, 枚举左端点l,然后找右端点r的可行位置, 我们发现如果从i 到 i + j可行, 而i + j + 1不可行, 那么在之后应定不会有可行的右端点, 因为a中区间最大值只会增加, 而b中区间的最小值只会减少, 所以右端点r的可行位置一定在一段连续的区间中。那么怎找到这段区间呢, 注意左端点确定时,最大值与最小值具有单调性, 所以如果我们利用RMQ欲处理出最大值和最小值, 就能二分把区间找到,具体如下: 如果a中(l, mid)大于b中(l, mid)的值, 那么(mid + 1, r)的值一定是不合法的, 所以查找区间(l, mid), 合法的值一定在(mid + 1, r)中, 查找(mid +1, r); 通过比较大于和大于等于找出区间的左端点和右端点, 更新答案(然而我这里之前没想通, wa了几百次) 所以RMQ预处理, 暴力枚举l, 二分r可行区间即可, 时间复杂度O(nlogn)
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int a[200100], b[200100], n; int st1[200100][21], st2[200100][21], p[21], lg[200100]; int rmq1(int i, int j) { int k = lg[j - i + 1]; return max(st1[i][k], st1[j - p[k] + 1][k]); } int rmq2(int i, int j) { int k = lg[j - i + 1]; return min(st2[i][k], st2[j - p[k] + 1][k]); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i ++)scanf("%d", &a[i]); for (int i = 1; i <= n; i ++)scanf("%d", &b[i]); int tmp = 2;p[0] = 1; for (int i = 1; i <= 20; i ++) p[i] = p[i - 1] * 2; for (int i = 2; i <= n; i ++) { lg[i] = lg[i - 1]; if (tmp <= i) lg[i] ++, tmp *= 2; //printf("%d %d\n", i, lg[i]); } for (int i = 1; i <= n; i ++) st1[i][0] = a[i], st2[i][0] = b[i]; for (int i = 1; i <= 20 && p[i] <= n; i ++) for (int j = 1; j <= n && j + p[i] - 1 <= n; j ++){ st1[j][i] = max(st1[j][i - 1], st1[j + p[i - 1]][i - 1]); st2[j][i] = min(st2[j][i - 1], st2[j + p[i - 1]][i - 1]); } long long ans = 0; for (int i = 1; i <= n; i ++){ if (a[i] > b[i]) continue; int l = i, r = n; while(l != r){ int mid = (l + r) >> 1; if (rmq1(i, mid) >= rmq2(i, mid)) r = mid; else l = mid + 1; } if (rmq1(i, r) != rmq2(i, l)) continue; int p = l; l = i, r = n + 1; while(l != r){ int mid = (l + r) >> 1; if (rmq1(i, mid) > rmq2(i, mid)) r = mid; else l = mid + 1;//printf("%d %d\n", l, r); } ans += l - p; // } printf("%I64d\n", ans); return 0; }
相关文章推荐
- SQL语句操作SQLite数据库Demo
- 使用VerticalBannerView实现垂直轮播广告(仿淘宝头条)
- TabLayout
- jsp前后台传值那点小事
- Android图片质量压缩以及缩略图生成
- 第二周-项目1C/C++语言中函数参数传递的三种方式
- 第一行代码系列第二章——手动创建activity
- 项目1-C/C++语言中函数参数传递的三种方式
- notifyDataSetChanged() 动态更新ListView 通过 Handler AsyncTask两种方式
- cuda 学习(三) Page-Locked Host Memory
- Poj 1061 青蛙的约会(扩展欧几里得)
- 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分
- 《TCP/IP详解 卷1:协议》 读书笔记 第6章 ICMP:Internet控制报文协议
- unity执行顺序问题(如何再次执行start方法)
- 第二周项目2 程序的多文件组织
- 视图和表
- 第四周《C语言及程序设计》实践项目22 用指针法访问数组元素
- 深入理解分布式事务
- 总结volley源码解析
- notifyDataSetChanged