题目
思路
n
≤
4096
n\le 4096
n≤4096
发现对于合并时候权值限制比较强然后放在值域上搞,
那么合并相当于值域上分两边选合起来
那么原序列
[
a
l
,
.
.
,
a
r
]
[a_l,..,a_r]
[al,..,ar] 在值域上是离散的,但是没关系,
我们可以分块
B
B
B 那么块和块之间直接相连时间复杂度
O
(
q
n
B
l
o
g
n
)
O(\frac{qn}{B}logn)
O(Bqnlogn)
我们尝试处理出
f
i
,
l
,
r
f_{i,l,r}
fi,l,r 表示第
i
i
i 块内位置在
[
l
,
.
.
,
r
]
[l,..,r]
[l,..,r] 的数合并在一起形成集合的编号
考虑块内分治:合并先归并合并
p
o
s
pos
pos 数组,暴力
l
e
n
2
len^2
len2 处理
f
f
f
时间复杂度:
T
(
n
)
=
2
∗
T
(
n
/
2
)
+
n
2
/
2
=
O
(
n
2
)
T(n)=2*T(n/2)+n^2/2=O(n^2)
T(n)=2∗T(n/2)+n2/2=O(n2) 那么总的
O
(
B
2
n
B
l
o
g
n
)
=
O
(
n
B
l
o
g
n
)
O(B^2\frac{n}{B}logn)=O(nBlogn)
O(B2Bnlogn)=O(nBlogn)
那么
B
=
q
B=\sqrt q
B=q 最优
l
o
g
log
log 是
l
o
w
e
r
b
o
u
n
d
lower_bound
lowerbound 的小常数
代码
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<climits>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int read(){
int f=0,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return f?-x:x;
}
const int MAXN=4096,MAXQ=65536,B=286;
vector<short> Val[8*MAXN+5];
vector<vector<int> > Id[8*MAXN+5];//Id[u][i][j]:u子树内编号[i...j(已经离散化)]的编号
int a[MAXN+5],pos[MAXN+5];
int ncnt,tot,c[(1<<23)+5],d[(1<<23)+5];
int Merge(int u,int v){
if(!u||!v) return u+v;
//printf("(%d %d)\n",u,v);
c[++tot]=u,d[tot]=v;
return tot;
}
int rt[B+5],ch[5*MAXN+5][2];
void Init(int u,int len){
Val[u].resize(len),Id[u].resize(len);
for(int i=0;i<len;i++)
Id[u][i].resize(len-i);
return ;
}
int Ask(int u,int L,int R){
if(R<Val[u].front()||Val[u].back()<L) return 0;
//printf("%d %d\n",L,R);
L=lower_bound(Val[u].begin(),Val[u].end(),L)-Val[u].begin();
R=upper_bound(Val[u].begin(),Val[u].end(),R)-Val[u].begin()-1;
//printf("<%d %d %d %d %d %d>\n",u,L,R,Id[u][L].size(),Val[u].size(),Id[u][L][R-L]);
return L>R?0:Id[u][L][R-L];
}
void Build(int &u,int L,int R){
if(!u) u=++ncnt;
if(L==R){
Init(u,1);
Val[u][0]=pos[L];
Id[u][0][0]=pos[L];
return ;
}
int Mid=(L+R)>>1;
Build(ch[u][0],L,Mid),Build(ch[u][1],Mid+1,R);
Init(u,Val[ch[u][0]].size()+Val[ch[u][1]].size());
merge(Val[ch[u][0]].begin(),Val[ch[u][0]].end(),Val[ch[u][1]].begin(),Val[ch[u][1]].end(),Val[u].begin());
int siz=Val[u].size();
for(int i=0;i<siz;i++)
for(int j=i;j<siz;j++){
int t1=Ask(ch[u][0],Val[u][i],Val[u][j]);
int t2=Ask(ch[u][1],Val[u][i],Val[u][j]);
Id[u][i][j-i]=Merge(t1,t2);
}
return ;
}
int Ans[MAXQ+5];
int main(){
int n=read(),q=read();
tot=n;
for(int i=1;i<=n;i++)
pos[a[i]=read()]=i;
for(int i=0;i*B<=n;i++)
Build(rt[i+1],i*B+1,min(n,(i+1)*B));
for(int i=1;i<=q;i++){
int L=read(),R=read();
for(int j=0;j*B<=n;j++)
Ans[i]=Merge(Ans[i],Ask(rt[j+1],L,R));
}
printf("%d\n",tot);
for(int i=n+1;i<=tot;i++)
printf("%d %d\n",c[i],d[i]);
for(int i=1;i<=q;i++)
printf("%d",Ans[i]),putchar(i==q?'\n':' ');
return 0;
}