Set Merging
题解
看到这道题,应该是很容易想到去分块的。
由于它要求合并两个集合时,一个的最小值必须是小于另一个的最大值,我们必须先对值域进行分块。
将任意一个block内所有区间都先更新出来,具体的合并操作可以在线段树上实现,我们可以在建树时就将区间的合并给更新出来。
之后每次查询的时候就只需要将各个块上的区间按序合并就可以了,容易证明,每次查询只需要合并
n
\sqrt{n}
n个块内的区间,由于
q
=
2
16
,
n
=
2
12
q=2^{16},n=2^{12}
q=216,n=212而又有重复合并的,总次数约为
n
q
n\sqrt{q}
nq。
总的合并次数大约为
2
n
q
2n\sqrt{q}
2nq,可以过。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define MAXN (1<<12)+5
#define MAXM (1<<22)+5
#define MAXD (1<<18)+5
typedef long long LL;
const int INF=0x7f7f7f7f;
const int n1=1<<8;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,q,tot,a[MAXN],b[MAXN],t[MAXN],ans[MAXD];
pii answ[MAXM];
int merge(int x,int y){if(!x||!y)return x+y;answ[tot]=make_pair(x,y);return ++tot;}
struct Tree{
vector<int> val;vector<vector<int> > id;
Tree(){}
Tree(int x){init(1);val[0]=b[x];id[0][0]=b[x]+1;}
void init(int x){val.resize(x);id.resize(x);for(int i=0;i<x;i++)id[i].resize(x-i);}
int ask(int l,int r)const{
if(r<val.front()||l>val.back())return 0;
l=lower_bound(val.begin(),val.end(),l)-val.begin();
r=upper_bound(val.begin(),val.end(),r)-val.begin()-1;
return l>r?0:id[l][r-l];
}
Tree update(const Tree &x,const Tree &y){
init(x.val.size()+y.val.size());
merge(x.val.begin(),x.val.end(),y.val.begin(),y.val.end(),val.begin());
int siz=val.size();
for(int i=0;i<siz;i++)
for(int j=i;j<siz;j++)
id[i][j-i]=merge(x.ask(val[i],val[j]),y.ask(val[i],val[j]));
return *this;
}
}T[MAXN/n1];
Tree solve(int l,int r){
//printf("solve%d %d\n",l,r);
if(l==r)return Tree(l);int mid=l+r>>1;Tree res;
return res.update(solve(l,mid),solve(mid+1,r));
}
signed main(){
read(n);read(q);tot=n--;
for(int i=0;i<=n;i++)read(a[i]),a[i]--,b[a[i]]=i;
for(int i=0;i<=n/n1;i++)T[i]=solve(i*n1,min(n,(i+1)*n1-1));
for(int i=1;i<=q;i++){
int l,r;read(l);read(r);l--;r--;
for(int j=0;j<=n/n1;j++)ans[i]=merge(ans[i],T[j].ask(l,r));
}
printf("%d\n",tot);
for(int i=n+1;i<tot;i++)printf("%d %d\n",answ[i].first,answ[i].second);
for(int i=1;i<=q;i++)printf("%d ",ans[i]);puts("");
return 0;
}