总得来说,思路就是先找到, L [ i ] L[i] L[i] 和 R [ i ] R[i] R[i] ,左右第一个满足偏序条件的索引,即 L [ i ] = j , s . t . j < i & & x [ j ] > = x [ i ] & & y [ j ] > = y [ i ] L[i] = j,s.t. j<i \quad \&\&\ x[j]>=x[i]\ \&\&\ y[j]>=y[i] L[i]=j,s.t.j<i&& x[j]>=x[i] && y[j]>=y[i] , R [ i ] R[i] R[i] 同理。找到之后,考虑每个位置 i i i 能为区间 [ l , r ] [l,r] [l,r] 产生的贡献,我们发现把询问 [ l , r ] [l,r] [l,r] 看成二维平面的点,那么 对于 i i i ,产生的贡献的区域就是 [ L [ i ] + 1 , i ] × [ i , R [ i ] − 1 ] [L[i]+1,i]\times [i,R[i]-1] [L[i]+1,i]×[i,R[i]−1] ,贡献为1。
所以,问题被分成两步:
1) 找到L[i],R[i]
2) 对二维区域进行加一操作,询问二维平面上1个点的值
对于第一部分,我们可以线段树二分查询,维护区间最大值。按
x
[
i
]
,
y
[
i
]
,
i
x[i],y[i],i
x[i],y[i],i 为关键字降序排序,对于每个
x
[
i
]
x[i]
x[i] 比它大的
x
x
x 已经被处理了,线段树里面放入的是
y
y
y 的值
坑点: 数据范围从0开始,简单的处理办法是一开始就把所有数值加1
对于第二部分,我们可以采取扫描线的方式,把询问离线下来,处理完询问需要的区域之后再进行算答案,扫描线的方式很多,可以写树状数组维护差分,那么只要实现单点修改,单点查询即可或者线段树实现区间修改,单点查询
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
constexpr int maxn = 1e6+10;
constexpr int inf = 1e7;
struct Event {
i64 x;
int y1, y2;
int type; // +1 for entering, -1 for leaving
bool operator<(const Event &p) const {
return x < p.x;
}
};
struct Scanline {
int n; // number of unique y-coordinates
vector<int> cnt; // cover count
vector<i64> len; // covered length
void init(){
n = 1000010;
len.assign(n<<2,0);
cnt.assign(n<<2,0);
}
// 向上维护节点覆盖长度
void pushup(int p, int l, int r) {
if (cnt[p] > 0) {
len[p] = r + 1 - l;
} else if (l == r) {
len[p] = 0;
} else {
len[p] = len[p << 1] + len[p << 1 | 1];
}
}
// 区间更新 [ql, qr]
void Update(int p, int l, int r, int ql, int qr, int val) { //ql,qr代表要更新的区间
if (ql > r || qr < l) return;
if (ql <= l && r <= qr) {
cnt[p] += val;
pushup(p, l, r);
return;
}
int mid = (l + r) >> 1;
Update(p << 1, l, mid, ql, qr, val);
Update(p << 1 | 1, mid + 1, r, ql, qr, val);
pushup(p, l, r);
}
int query(int p,int l,int r,int x){
if(l==r) return cnt[p];
int mid = (l+r)>>1;
int res = cnt[p];
if(x <= mid) return res + query(p<<1, l, mid, x);
else return res + query(p<<1|1, mid+1, r, x);
}
vector<int> calc(vector<Event> &events,vector<array<int,3>> &qr) {
sort(events.begin(), events.end());
vector<int> ans(qr.size());
int now = 0;
for (int i = 0; i < (int)events.size(); i++) {
while(now<qr.size()&&qr[now][1]<events[i].x){
ans[qr[now][2]] = query(1,0,n-2,qr[now][0]);
++now;
}
Update(1,0,n-2,events[i].y1,events[i].y2,events[i].type);
}
while(now<qr.size()){
ans[qr[now][2]] = query(1,0,n-2,qr[now][0]);
++now;
}
return ans;
}
}sc;
int n,q,L[maxn],R[maxn];
array<int,3> a[maxn],b[maxn];
int tree[maxn<<2];
void build(int p,int l,int r){
tree[p]=-1;
if(l==r){
tree[p]=0;
return;
}
int mid =(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void update(int p,int l,int r,int x,int num){
if(x==l&&l==r){
tree[p]=num;
return;
}
int mid=(l+r)/2;
if(x<=mid) update(p*2,l,mid,x,num);
else update(p*2+1,mid+1,r,x,num);
tree[p]=max(tree[p*2],tree[p*2+1]);
}
int query_left(int p,int l,int r,int x,int y,int num){//左侧最大的应该先找右侧找不到再找左侧
if(x>r||y<l) return -1;
if(x<=l&&r<=y){
if(tree[p]<num) return -1;
if(l==r) return l;
}
int mid = (l+r)>>1;
int res = query_left(p<<1|1,mid+1,r,x,y,num);
if(res!=-1) return res;
return query_left(p<<1,l,mid,x,y,num);
}
int query_right(int p,int l,int r,int x,int y,int num){
if(x>r||y<l) return -1;
if(x<=l&&r<=y){
if(tree[p]<num) return -1;
if(l==r) return l;
}
int mid = (l+r)>>1;
int res = query_right(p<<1,l,mid,x,y,num);
if(res!=-1) return res;
return query_right(p<<1|1,mid+1,r,x,y,num);
}
void solve(){
cin>>n>>q;
for(int i =1 ;i<=n;++i) cin>>a[i][0];
for(int i = 1;i<=n;++i){
cin>>a[i][1];
a[i][2] = -i;
b[i]=a[i];
}
a[0]=a[n+1]={inf,inf,-1};
sort(b+1,b+n+1,greater<array<int,3>> ());
memset(tree,-1,sizeof tree);
update(1,1,n+2,1,inf);//0->1
update(1,1,n+2,n+2,inf);
for(int i = 1,j=1;i<=n;++i){
while(j<=n&&b[j][0]>=b[i][0]){
int idx = -b[j][2]+1;
update(1,1,n+2,idx,b[j][1]);
++j;
}
L[-b[i][2]] = query_left(1,1,n+2,1,-b[i][2],b[i][1])-1;
}
memset(tree,-1,sizeof tree);
update(1,1,n+2,1,inf);//0->1
update(1,1,n+2,n+2,inf);
for(int i =1,j=1;i<=n;++i){
while(j<=n&&b[j][0]>=b[i][0]){
int idx = -b[j][2]+1;
update(1,1,n+2,idx,b[j][1]);
++j;
}
R[-b[i][2]] = query_right(1,1,n+2,-b[i][2]+2,n+2,b[i][1])-1;
}
vector<array<int,3>> qr(q);
for(int i = 0;i<q;++i){
cin>>qr[i][0]>>qr[i][1];
qr[i][2] = i;
}
sort(qr.begin(),qr.end(),[&](auto &A,auto & B){
return A[1]<B[1];
});
//扫描线部分
vector<Event> events;
for(int i = 1;i<=n;++i){//[L[i]+1,i] 叉乘 [i,R[i]-1]
events.emplace_back(i,L[i]+1,i,1);
events.emplace_back(R[i],L[i]+1,i,-1);
}
sc.init();
vector<int> ans = sc.calc(events,qr);
for(auto &i:ans) cout<<i<<"\n";
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
int t=1;
//cin>>t;
while(t--) solve();
return 0;
}
/*
10 1
1 0 7 9 3 0 4 7 5 9
7 7 2 0 4 4 0 5 9 7
*/