Count[题解](枚举+线段树二分+主席树)
题目描述
给定一个长度为 \(n\) 的序列 \(a_{1\cdots n}\),定义 \((i,j)\)(规定 \(i<j\))为好点对,当且仅当满足下列条件之一:
- \(i=j-1\)
- \(\forall k\in(i,j),a_k < \min\{a_i,a_j\}\)
现在有\(m\)组询问区间 \([l,r]\) 内好点对的个数。
给定一个 \(type\),当 \(type=0\) 的时候不强制在线,否则强制在线,具体操作请看输入格式。
输入格式
第一行三个整数 \(n,m,type\),意义同题目描述。
第二行 \(n\) 个整数 \(a_{1\cdots n}\).
接下来 \(m\) 行,每行两个非负整数 \(l,r\)。当 \(type=0\),询问区间就是 \([l,r]\),否则令 \(u=(l+last-1)\mod n+1,v=(r+last-1)\bmod n+1\),那么当前询问区间就是 \([\min\{u,v\},\max\{u,v\}]\),其中 \(last\) 是上一次询问的答案,初始时 \(last=0\)。
输出格式
共 \(m\) 行,第 \(i\) 行一个非负整数表示第\(i\)次询问的答案。
样例
输入
3 2 0
2 1 2
1 1
1 3
输出
0
3
解题思路
看到题面,我们或许可以想到通过枚举某一个点所造成的贡献来计算区间内的总贡献,刚看到题的我有如下暴力思路:
我们在\([l,r]\)区间内枚举左端点\(a_i\),接着暴力向右扩展,并时刻记录所遇到\(a_j\)的最大值,若遇到了一个\(a_j>\max_{k\in(i,j)}{a_k}\),则让\(ans+1\),若出现了\(a_j>a_i\),则将循环终止
时间复杂度\(O(n^{3})\)
code
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=3e5+10;
int n,m,op,a[N],ans;
signed main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>op;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,l,r,u,v;i<=m;i++) {
cin>>u>>v;
if(op) u=(u+ans-1)%n+1,v=(v+ans-1)%n+1;
l=min(u,v),r=max(u,v);
ans=0;
for(int j=l;j<=r;j++) {
int mx=-inf;
for(int k=j+1;k<=r;k++) {
if(a[k]>mx) ans++;
mx=max(mx,a[k]);
if(a[k]>=a[j]) break;
}
}
cout<<ans<<"\n";
}
return 0;
}
其实上面的暴力思路已经提示我们了,上面是枚举\(i\)作为连续段左端点的情况,那我们是否可以考虑枚举\(a_i=\min \{a_i,a_j\}\)的情况呢?
由于不确定\(a_i\)是作左端点还是右端点,我们向左向右扩展\(a_j\),如果\(a_j<a_i\)那么这个\(j\)就不能作贡献,如果\(a_j>=a_i\),那么这个\(j\)就能作贡献,且左边的点会因为中间已经出现了大于\(a_i\)的点而不能作贡献,因此,当我们钦定\(a_i=\min \{a_i,a_j\}\)时,能作贡献的\(j\)至多有两个,左一个右一个

因此对于每一个\(i\),我们只需要找到左边第一个大于\(a_i\)的点和右边第一个大于\(a_i\)的点,再在主席树上维护起来就可以了
具体的,我们先正序枚举再倒序枚举,用一颗普通线段树(也可以用单调栈线性做)维护到当前位置的区间最值,再在线段树上二分找到最靠右(左)的大于\(a_i\)的点,再在主席树上\(i\)这个历史版本中插入两个位置(若没有插入到\(0\)或者\(n+1\)位置上就好了),查询时在用在\([l,r]\)上前缀和一减就好了(保证两个端点都在\([l,r]\),内)。
细节:
1.若左右端点的值相同,我们会重复计算,因此当这两个值相同时,我们只取\(i\)作右端点的情况(也可以是左端点)
2.若没有离散化的话要注意线段树上\(max\)的初值,因为\(a_i\)可能是负数
code
#include<bits/stdc++.h>
#define lson tr[rt].ls
#define rson tr[rt].rs
#define olson tr[old].ls
#define orson tr[old].rs
#define inf 0x3f3f3f3f
using namespace std;
const int N=3e5+10;
int n,m,tot,op,a[N],b[N],ans,root[N],pre[N],nxt[N];
struct SegmentTree {int ls,rs,sum,mx;} tr[N*50];
void pushup(int rt) {
tr[rt].mx=max(tr[lson].mx,tr[rson].mx);
tr[rt].sum=tr[lson].sum+tr[rson].sum;
}
void add(int &rt,int l,int r,int pos,int val) {
if(!rt) rt=++tot;
if(l==r) return tr[rt].mx=val,void();
int mid=(l+r)>>1;
if(pos<=mid) add(lson,l,mid,pos,val);
else add(rson,mid+1,r,pos,val);
pushup(rt);
}
int ask1(int rt,int l,int r,int val) {
if(!rt) return 0;
if(l==r) return l;
int mid=(l+r)>>1;
if(tr[rson].mx>=val) return ask1(rson,mid+1,r,val);
else return ask1(lson,l,mid,val);
}
int ask2(int rt,int l,int r,int val) {
if(!rt) return 0;
if(l==r) return l;
int mid=(l+r)>>1;
if(tr[lson].mx>=val) return ask2(lson,l,mid,val);
else return ask2(rson,mid+1,r,val);
}
void update(int &rt,int old,int l,int r,int pos) {
tr[++tot]=tr[rt],rt=tot,lson=olson,rson=orson;
if(l==r) return tr[rt].sum++,void();
int mid=(l+r)>>1;
if(pos<=mid) update(lson,olson,l,mid,pos);
else update(rson,orson,mid+1,r,pos);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R) {
if(!rt) return 0;
if(L<=l&&r<=R) return tr[rt].sum;
int mid=(l+r)>>1,ans=0;
if(L<=mid) ans+=query(lson,l,mid,L,R);
if(R>mid) ans+=query(rson,mid+1,r,L,R);
return ans;
}
signed main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>op;
for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i];
sort(b+1,b+1+n);
int idx=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+idx,a[i])-b;
add(root[n+1],0,n+1,0,n+1);
for(int i=1;i<=n;i++) {
pre[i]=ask1(root[n+1],0,n+1,a[i]);
add(root[n+1],0,n+1,i,a[i]);
}
add(root[n+2],0,n+1,n+1,n+1);
for(int i=n;i>=1;i--) {
nxt[i]=ask2(root[n+2],0,n+1,a[i]);
add(root[n+2],0,n+1,i,a[i]);
}
for(int i=1;i<=n;i++) {
if(a[i]==a[nxt[i]]) update(root[i],root[i-1],0,n+1,pre[i]);
else update(root[i],root[i-1],0,n+1,pre[i]),update(root[i],root[i],0,n+1,nxt[i]);
}
int ans=0;
for(int i=1,u,v,l,r;i<=m;i++) {
cin>>u>>v;
if(op) u=(u+ans-1)%n+1,v=(v+ans-1)%n+1;
l=min(u,v),r=max(u,v);
ans=query(root[r],0,n+1,l,r)-query(root[l-1],0,n+1,l,r);
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号