题意:给定两个整数序列a,b,将a,b对齐,问有多少个区间满足a的区间内最大值等于b的区间内最小值。

数据范围:区间长度n属于[1, 200000],序列中的元素在整型范围内

思路:枚举所有n*(n+1)/2个区间复杂度过高。题解的方法是,只枚举区间左端点,然后想办法把对右端点的处理降到O(logn)。能降得益于这道题特有的以下性质:

首先,枚举每个左端点时,将左端点left定义为一个常量,将右端点r定义为变量,r >= left;故题目的两个要求可以翻译为这样两个以右端点r为自变量的函数 max{ar}与min{br},分别表示序列a在区间[left, r]上的最大值,序列b在区间[left, r]上的最小值。

其次,有如下观察事实:

1. 固定左端点,则随着区间的向右扩张,max{ai}不会变小,min{bi}不会变大;即max{ar}单调不降,min{br}单调不升

2. 根据1,可以推出一旦扩张到某个位置出现max{ai} > min{bi},那么再往右扩张就已经没意义了;对称的,若当前位置仍处在max{ar} < min{br}的状态,那么符合条件的右边界r若存在则一定在当前位置右侧。

3. 根据2,可以推出符合条件的右边界r如果存在,则一定连续分布在left右侧的某个区间内。

所以,根据以上性质,尤其第3条,我们便可以使用二分查找来加速原来的对右边界的枚举。假设合法的右边界构成了区间[rmin, rmax],那么分别二分查找rmin, rmax即可。类似lower_bound和upper_bound,对rmin和rmax二分查找的区别仅在于相等时的移动方向:rmin左移而rmax右移。另外注意对查找失败情况的处理,查找前初始化rmin为n-1, rmax为left,这样查找失败<=>rmax < rmin,这种情况不为最终的结果贡献长度。

这样确定一个rmin需要二分logn个位置*每个位置logn的求最值,共计log2n;因此总的时间为n*2log2n = n*log2(n2)

区间查询部分题解说用任意一个可以做RMQ的数据结构即可,于是想借此试试线段树,结果T了。。。然后剪枝,当rmin不合法时continue。然而还是会T,因为最坏情况无法避免n*2log2n的总时间。于是学习别人的姿势改用sparse table,这样需nlogn的预处理,但每个位置求最值只需O(1),所以总的时间为nlogn + n,最坏情况确实比线段树更快。

 #include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <assert.h>
#define FREAD(fn) freopen((fn), "r", stdin)
#define RINT(vn) scanf("%d", &(vn))
#define PINT(vb) printf("%d", vb)
#define RSTR(vn) scanf("%s", (vn))
#define PSTR(vn) printf("%s", (vn))
#define CLEAR(A, X) memset(A, X, sizeof(A))
#define REP(N) for(int i=0; i<(N); i++)
#define REPE(N) for(int i=1; i<=(N); i++)
#define pb(X) push_back(X)
#define pn() printf("\n")
using namespace std;
const int MAX_N = (<<);//注意要把最大值扩展到2的幂次
const int INFP = 0x7fffffff;
const int INFN = -0x7fffffff; int n, m;//m为大于n的最小的2的幂
int a[MAX_N], b[MAX_N];
int ta[MAX_N][], tb[MAX_N][];//spare table, t[i][j],i为起点,2^j为区间长度 void build_max(){
for(int i=n; i<m; i++) a[i] = INFN;//负无穷填充
REP(m) ta[i][] = a[i];
for(int j=; j<__builtin_ctz(m); j++){//m即区间长度的上限
for(int i=; i+(<<j) <= m; i++){
ta[i][j] = max(ta[i][j-], ta[i+(<<(j-))][j-]);
}
}
} void build_min(){
for(int i=n; i<m; i++) b[i] = INFP;//正无穷填充
REP(m) tb[i][] = b[i];
for(int j=; j<__builtin_ctz(m); j++){//m即区间长度的上限
for(int i=; i+(<<j) <= m; i++){
tb[i][j] = min(tb[i][j-], tb[i+(<<(j-))][j-]);
}
}
} //闭区间
int qmax(int l, int r){
int k = log(r-l+)/log(2.0);
return max(ta[l][k], ta[r-(<<k)+][k]);
} int qmin(int l, int r){
int k = log(r-l+)/log(2.0);
return min(tb[l][k], tb[r-(<<k)+][k]);
} //左闭右开,l为起始点
int lowerbound(int l){
int lo = l, hi = n;//初始左右界桩
int ans = n;//失败返回右界桩
while(lo < hi){
int mi = (lo+hi)/;
int qa = qmax(l, mi);
int qb = qmin(l, mi);
if(qa > qb) hi = mi;
else if(qa < qb) lo = mi+;
else{
ans = min(ans, mi);//命中而左移和未命中而左移是不同的!
hi = mi;
} }
return ans;
}
int upperbound(int l){
int lo = l, hi = n;
int ans = -;
while(lo < hi){
int mi = (lo+hi)/;
int qa = qmax(l, mi);
int qb = qmin(l, mi);
if(qa > qb) hi = mi;
else if(qa < qb) lo = mi+;
else{
ans = max(ans, mi);
lo = mi+;
}
}
return ans;
} int main(){
//FREAD("689d.txt");
RINT(n);
m = ;
while(m < n) m <<= ;//扩展为2的幂
REP(n) RINT(a[i]);
REP(n) RINT(b[i]);
build_max();
build_min();
__int64 ans = ;
int rmin = , rmax = ;
REP(n){//for each left end = i, enumerate rmin, rmax
rmin = lowerbound(i);
rmax = upperbound(i);
if(rmin <= rmax)
ans += rmax - rmin + ;
//printf("left = %d, rmin = %d, rmax = %d\n", i, rmin, rmax);
}
cout << ans;
return ;
 #include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <assert.h>
#define FREAD(fn) freopen((fn), "r", stdin)
#define RINT(vn) scanf("%d", &(vn))
#define PINT(vb) printf("%d", vb)
#define RSTR(vn) scanf("%s", (vn))
#define PSTR(vn) printf("%s", (vn))
#define CLEAR(A, X) memset(A, X, sizeof(A))
#define REP(N) for(int i=0; i<(N); i++)
#define REPE(N) for(int i=1; i<=(N); i++)
#define pb(X) push_back(X)
#define pn() printf("\n")
using namespace std;
const int MAX_N = (<<);//注意要把最大值扩展到2的幂次
const int INFP = 0x7fffffff;
const int INFN = -0x7fffffff; struct Node
{
int l, r;
int v;
Node(){}
}; int n, m;//m为大于n的最小的2的幂
int a[MAX_N], b[MAX_N];
Node sta[MAX_N*], stb[MAX_N*];//这个是segment tree void build_max(int A[], Node AT[], int N){
m = ;
while(m < N) m <<= ;//m个叶节点,m-1个内部节点,下标从1开始
for(int i=; i<N; i++){
RINT(AT[m+i].v);
//AT[m+i].v = A[i];//复制叶节点到m-2m
AT[m+i].l = AT[m+i].r = i;
}
for(int i=N; i<m; i++){
AT[m+i].v = INFN;//末尾用负无穷填充
AT[m+i].l = AT[m+i].r = i;
}
for(int i=m-; i>=; i--){//自底向上生成内部节点
AT[i].v = max(AT[i*].v, AT[i*+].v);
AT[i].l = AT[i*].l;
AT[i].r = AT[i*+].r;
}
// for(int i=1; i<=m*2-1; i++)
// printf("%d %d\n", i, AT[i].v);
} void build_min(int A[], Node AT[], int N){
m = ;
while(m < N) m <<= ;//m个叶节点,m-1个内部节点,下标从1开始
for(int i=; i<N; i++){
RINT(AT[m+i].v);
//AT[m+i].v = A[i];//复制叶节点到m-2m
AT[m+i].l = AT[m+i].r = i;
}
for(int i=N; i<m; i++){
AT[m+i].v = INFP;//末尾用正无穷填充
AT[m+i].l = AT[m+i].r = i;
}
for(int i=m-; i>=; i--){//自底向上生成内部节点
AT[i].v = min(AT[i*].v, AT[i*+].v);
AT[i].l = AT[i*].l;
AT[i].r = AT[i*+].r;
}
// for(int i=1; i<=m*2-1; i++)
// printf("%d %d\n", i, AT[i].v);
} //闭区间,cur为当前子树根
int qmax(int cur, int l, int r){//其实l, r在全局的查询中不会变
if(l <= sta[cur].l && sta[cur].r <= r){
//printf("hit [%d, %d]\n", sta[cur].l, sta[cur].r);
return sta[cur].v;//当前区间包含在目标区间内
}
if(sta[cur].r < l || sta[cur].l > r) return INFN;//不相交则不再递归
else return max(qmax(cur*, l, r), qmax(cur*+, l, r));
} int qmin(int cur, int l, int r){
if(l <= stb[cur].l && stb[cur].r <= r) return stb[cur].v;
if(stb[cur].r < l || stb[cur].l > r) return INFP;
else return min(qmin(cur*, l, r), qmin(cur*+, l, r));//原来min是先算右边的,再算左边的
} //左闭右开,l为起始点
int lowerbound(int lo, int hi, int l){
int ans = n;
while(lo < hi){
int mi = (lo+hi)/;
int qa = qmax(, l, mi);
int qb = qmin(, l, mi);
if(qa > qb) hi = mi;
else if(qa < qb) lo = mi+;
else{
ans = min(ans, mi);//命中而左移和未命中而左移是不同的!
hi = mi;
} }
return ans;
}
int upperbound(int lo, int hi, int l){
int ans = ;
while(lo < hi){
int mi = (lo+hi)/;
int qa = qmax(, l, mi);
int qb = qmin(, l, mi);
if(qa > qb) hi = mi;
else if(qa < qb) lo = mi+;
else{
ans = max(ans, mi);
lo = mi+;
}
}
return ans;
} int main(){
FREAD("689d.txt");
RINT(n);
build_max(a, sta, n);
build_min(b, stb, n);
__int64 ans = ;
int rmin = , rmax = ;
REP(n){//for each left end = i, enumerate rmin, rmax
rmin = lowerbound(i, n, i);
if(rmin >= i && rmin < n){//剪枝,rmin存在,则rmax存在
rmax = upperbound(rmin, n, i);
ans += rmax - rmin + ;
}
//if(n == 190593 && i == n/2) break;//这个是为了测试时间,发现跑了一半已经快超时了
printf("left = %d, rmin = %d, rmax = %d\n", i, rmin, rmax);
}
cout << ans;
return ;
}

T了的线段树

【CF689D Friends and Subsequences】二分搜索,区间查询的更多相关文章

  1. codeforces 597C C. Subsequences(dp+树状数组)

    题目链接: C. Subsequences time limit per test 1 second memory limit per test 256 megabytes input standar ...

  2. [LeetCode] Largest BST Subtree 最大的二分搜索子树

    Given a binary tree, find the largest subtree which is a Binary Search Tree (BST), where largest mea ...

  3. [LeetCode] Distinct Subsequences 不同的子序列

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  4. Distinct Subsequences

    https://leetcode.com/problems/distinct-subsequences/ Given a string S and a string T, count the numb ...

  5. HDU 2227 Find the nondecreasing subsequences (DP+树状数组+离散化)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2227 Find the nondecreasing subsequences             ...

  6. Leetcode Distinct Subsequences

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  7. Thinkphp查询 1.查询方式 2.表达式查询 3.快捷查询 4.区间查询 5.组合查询 6.统计查询 7.动态查询 8.SQL 查询

    1.使用字符串作为条件查询 $user = M('User'); var_dump($user->where('id=1 AND user="蜡笔小新"')->sele ...

  8. LeetCode(115) Distinct Subsequences

    题目 Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequen ...

  9. [Leetcode][JAVA] Distinct Subsequences

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

随机推荐

  1. windows puppet manifests 文件维护

    初级 puppet windows agent实现简单的msi格式安装包安装及bat文件创建;

  2. Python Scrapy安装杂症记录

    昨天安装了scrapy一切正常,调试了bbsSpider案例(详见上文),今日开机因为冰封还原,提示找不到python27.dll,重新安装了python2.7, 使用easy-install scr ...

  3. 第23讲 UI_布局 之相对布局

    第23讲 UI_布局 之相对布局 .RelativeLayout(相对布局): RelativeLayout(相对布局)是指组件的位置总是相对兄弟组件.父容器来决定的(相对位置),如某个组件的左边右边 ...

  4. hdu 5423 Rikka with Tree(dfs)

    Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he ...

  5. ubuntu14.04 cocos2d-x-3.6 glfw编译出错解决方案

    lib/libcocos2d.a(CCGLViewImpl-desktop.cpp.o): In function `cocos2d::GLViewImpl::GLViewImpl()': /home ...

  6. 1042. Shuffling Machine (20) - sstream实现数字转字符串

    题目例如以下: Shuffling is a procedure used to randomize a deck of playing cards. Because standard shuffli ...

  7. 502 bad gateway是什么意思

    通俗解释一下 1.什么是502 bad gateway 报错? 简单来说 502 是报错类型代码 bad gateway 错误的网关 2.产生错误的原因 连接超时 我们向server器发送请求 因为s ...

  8. 如何判断Linux load的值是否过高

    1.先使用top看下CPU占用高的进程,找出进程的进程ID(pid): 查看方法:top 2.根据进程ID(pid)查看是进程的那些线程占用CPU高. 查看方法:top -Hp pid 3.使用pst ...

  9. android-用xml自定义背景(可自定义显示具体那一边)

    常见的描边都是闭合的.四个边都有.如下: <?xml version="1.0" encoding="UTF-8"?> <layer-list ...

  10. linux下mysql环境支持中文配置步骤

    sql脚本执行前加上: CREATE DATABASE IF NOT EXISTS mydatabase DEFAULT CHARSET utf8 COLLATE UTF8_GENERAL_CI; u ...