考虑将询问区间按右端点排序。考虑x会与它前面产生贡献的点对,显然,如果 i<j<x,ai>aj>ax ,那么(i,x)就是无用的。也就是说我们要求位置 < x且权值大于 ax 的点和小于 ax 的点的两个类似笛卡尔树的序列的东西,而这个在随机数据中显然是 O(logn) 的。
但是问题是怎么求这个东西呢?一个比较脑残的想法是线段树!就是我们存一棵权值线段树,保存的是权值在一个范围内的编号最靠后的。这样我们每次取一个最靠后的。
然后这样常数巨大跑了10s。。
另一个比较简单的做法是考虑我们要生成大于的那个序列,我们生成到了y,那么实际上我们就是要找小于y的序列中第一个大于 ax 的。而这当然是可以二分的,所以我们只需要 O(nlognloglogn) 就可以生成这个蛋疼的序列了!
但是并不要高兴太早,因为我们还需要支持 O(nlogn) 个插入和 O(q) 个后缀min查询。。这个可以树状数组或分块,跑起来差不多。
时间复杂度就是 O(nlognloglogn+nlog2n+qlogn) 或 O(nlognlogn+nlogn+qn√) (求序列的那个 O(lognloglogn) 其实跟 O(log2n) 差别不大)。
这题还有一个很蛋疼的地方就是答案不计0…(估计是出题人写残了吧rofl)。
代码(线段树):
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;
int n;
int hash[N];
int a[N];
int segt[N<<2];
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void add(int node,int l,int r,int x){
segt[node]=x;
if(l==r)return;
if(a[x]<=l+r>>1)add(lson,x);
else add(rson,x);
}
int query(int node,int l,int r,int L,int R){
if(L<=l&&r<=R)return segt[node];
int ans=0;
if(L<=l+r>>1)ans=max(ans,query(lson,L,R));
if(R>l+r>>1)ans=max(ans,query(rson,L,R));
return ans;
}
#define inf 0x7fffffff
int bit[N];
void add(int x,int A){
//cout<<"add:"<<x<<","<<A<<endl;
for(;x;x-=x&-x)bit[x]=min(bit[x],A);
}
int query(int x){
//cout<<"query("<<x<<")\n";
int ans=inf;
for(;x<=n;x+=x&-x){
ans=min(ans,bit[x]);
//cout<<"bit["<<x<<"]="<<bit[x]<<endl;
}
return ans;
}
struct QS{
int l,r,i;
bool operator < (const QS & o)const{
return r<o.r;
}
}que[Q];
int ans[Q];
int main(){
int q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
hash[i]=a[i];
}
sort(hash+1,hash+n+1);
int htot=unique(hash+1,hash+n+1)-hash;
for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
--htot;
for(int i=0;i<q;++i){
scanf("%d%d",&que[i].l,&que[i].r);
que[i].i=i;
}
sort(que,que+q);
que[q].r=-1;
memset(bit,127,sizeof(bit));
int x;
for(int i=1,j=0;i<=n;++i){
for(x=query(1,1,htot,a[i],htot);x&&a[x]!=a[i];){
add(x,hash[a[x]]-hash[a[i]]);
x=query(1,1,htot,a[i],a[x]-1);
}
for(x=query(1,1,htot,1,a[i]);x&&a[x]!=a[i];){
add(x,hash[a[i]]-hash[a[x]]);
x=query(1,1,htot,a[x]+1,a[i]);
}
add(1,1,htot,i);
for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l);
}
for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}
代码(bit):
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;
int n;
int hash[N];
int a[N];
int big[N][30],sml[N][30];
#define inf 0x7fffffff
int bit[N];
void add(int x,int A){
//cout<<"add:"<<x<<","<<A<<endl;
for(;x;x-=x&-x)bit[x]=min(bit[x],A);
}
int query(int x,int n){
//cout<<"query("<<x<<")\n";
int ans=inf;
for(;x<=n;x+=x&-x){
ans=min(ans,bit[x]);
//cout<<"bit["<<x<<"]="<<bit[x]<<endl;
}
return ans;
}
struct QS{
int l,r,i;
bool operator < (const QS & o)const{
return r<o.r;
}
}que[Q];
int ans[Q];
int main(){
int q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
hash[i]=a[i];
}
sort(hash+1,hash+n+1);
int htot=unique(hash+1,hash+n+1)-hash;
for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
--htot;
for(int i=0;i<q;++i){
scanf("%d%d",&que[i].l,&que[i].r);
que[i].i=i;
}
sort(que,que+q);
que[q].r=-1;
int tmp=0;
memset(bit,127,sizeof(bit));
bool flag;
int x;
for(int i=1,j=0;i<=n;++i){
//printf("-------%d------\n",i);
flag=0;
for(int k=i;--k;)
if(a[k]>a[i]){
big[i][++big[i][0]]=k;
flag=1;
break;
}
for(x=big[i][1];flag;){
flag=0;
for(int k=1;k<=sml[x][0];++k)
if(a[sml[x][k]]>a[i]){
big[i][++big[i][0]]=sml[x][k];
flag=1;
x=sml[x][k];
break;
}
}
flag=0;
for(int k=i;--k;)
if(a[k]<a[i]){
sml[i][++sml[i][0]]=k;
flag=1;
break;
}
for(x=sml[i][1];flag;){
flag=0;
for(int k=1;k<=big[x][0];++k)
if(a[big[x][k]]<a[i]){
sml[i][++sml[i][0]]=big[x][k];
x=big[x][k];
flag=1;
break;
}
}
tmp=max(tmp,max(big[i][0],sml[i][0]));
for(int k=big[i][0];k;--k)add(big[i][k],hash[a[big[i][k]]]-hash[a[i]]);
for(int k=sml[i][0];k;--k)add(sml[i][k],hash[a[i]]-hash[a[sml[i][k]]]);
for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l,i);
}
for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}
代码(分块):
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=1e5+5,Q=1e5+5;
int n;
int hash[N];
int a[N];
int big[N][30],sml[N][30];
#define inf 0x7fffffff
const int S=400;
int block[N/S+5];
int b[N];
void add(int x,int A){
b[x]=min(b[x],A);
block[x/S]=min(block[x/S],A);
}
int query(int l,int r){
int ans=inf;
for(int i=l/S,rs=r/S;++i<=rs;)ans=min(ans,block[i]);
for(int i=min((l/S+1)*S,r+1);--i>=l;)ans=min(ans,b[i]);
return ans;
}
struct QS{
int l,r,i;
bool operator < (const QS & o)const{
return r<o.r;
}
}que[Q];
int ans[Q];
int main(){
int q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
hash[i]=a[i];
}
sort(hash+1,hash+n+1);
int htot=unique(hash+1,hash+n+1)-hash;
for(int i=n;i;--i)a[i]=lower_bound(hash+1,hash+htot,a[i])-hash;
--htot;
for(int i=0;i<q;++i){
scanf("%d%d",&que[i].l,&que[i].r);
que[i].i=i;
}
sort(que,que+q);
que[q].r=-1;
int tmp=0;
memset(b,127,sizeof(b));
memset(block,127,sizeof(block));
bool flag;
int x;
for(int i=1,j=0;i<=n;++i){
//printf("-------%d------\n",i);
flag=0;
for(int k=i;--k;)
if(a[k]>a[i]){
big[i][++big[i][0]]=k;
flag=1;
break;
}
for(x=big[i][1];flag;){
flag=0;
for(int k=1;k<=sml[x][0];++k)
if(a[sml[x][k]]>a[i]){
big[i][++big[i][0]]=sml[x][k];
flag=1;
x=sml[x][k];
break;
}
}
flag=0;
for(int k=i;--k;)
if(a[k]<a[i]){
sml[i][++sml[i][0]]=k;
flag=1;
break;
}
for(x=sml[i][1];flag;){
flag=0;
for(int k=1;k<=big[x][0];++k)
if(a[big[x][k]]<a[i]){
sml[i][++sml[i][0]]=big[x][k];
x=big[x][k];
flag=1;
break;
}
}
tmp=max(tmp,max(big[i][0],sml[i][0]));
for(int k=big[i][0];k;--k)add(big[i][k],hash[a[big[i][k]]]-hash[a[i]]);
for(int k=sml[i][0];k;--k)add(sml[i][k],hash[a[i]]-hash[a[sml[i][k]]]);
for(;que[j].r==i;++j)ans[que[j].i]=query(que[j].l,i);
}
for(int i=0;i<q;++i)printf("%d\n",ans[i]);
}
总结:
①对于大O复杂度的东西开数组时一定要想清常数。
②要注意划归子问题,利用已求的东西。