Link.
Description.
有一个长度为 \(n(n\le 2^{12})\) 排列,和 \(Q(Q\le 2^{16})\) 个要求。
初始有 \(\{\{a_1\},\{a_2\},\cdots,\{a_n\}\}\) 这个大集合。
有一种操作:
- 选择 \(S,T\) 两个集合,满足 \(\max\{S\}\le \min\{T\}\),把 \(S\cup T\) 加入大集合。
每个要求形如:
- 在大集合中存在 \([l,r]\) 所有数构成的集合。
在每个瞬间最多只能有 \(2.2\times 10^6\) 个集合。
Solution.
\(2^{21}\approx 2\times 10^6\),猜测最后复杂度是 \(O(2n\sqrt q)\) 的。
对于每个询问,会被拆成 \(\frac nB\) 个块,如果我们每个块所对应的子区间全都处理好就可以 \(O(\frac nB)\) 合并,总复杂度 \(O(\frac{qn}B)\)
值域分块,对于每个块内先合并出每个子区间,要求总复杂度 \(O(\frac nB\times B^2)=O(nB)\)。
考虑值域分治,两个子问题很好处理,然后合并直接 \(O(n^2)\) 合并,复杂度 \(O(T)=2O(\frac T2)+O(n^2)=O(n^2)\),总复杂度 \(O(\frac nBB^2)=O(nB)\)
当 \(B=\sqrt Q\) 时最优,总复杂度为 \(O(2n\sqrt q)\)
Coding.
刚开始每个块里开了 \(B*B\) 的静态数组,MLE 了,以为是这个的锅,就换成 vector 了
点击查看代码
#include<bits/stdc++.h>//{{{
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
const int N=1<<12|5,B=1<<8,Lim=2.2e6;
int n,q,a[N],wh[N],rsx[Lim],rsy[Lim],rst,rsid[1<<16|5];
inline int mrg(int x,int y) {return !x||!y?x|y:(rsx[++rst]=x,rsy[rst]=y,rst);}
struct block
{
int len,pos[B+5];vector<vector<int> >id;
inline void init(int x) {len=x,id.resize(x+1);for(int i=1;i<=x;i++) id[i].resize(x+1);}
block(int x=0) {if(x) init(1),id[1][1]=pos[1]=wh[x];}
inline int qryid(int l,int r)
{//这个是值域区间
if(r<pos[1]||l>pos[len]) return 0;
int le=lower_bound(pos+1,pos+len+1,l)-pos;
int ri=upper_bound(pos+1,pos+len+1,r)-pos-1;
return le>ri?0:id[le][ri];
}
inline block operator+(block y)
{
block rs;rs.init(len+y.len);//guard that *this < y
for(int i=1;i<=len;i++) rs.pos[i]=pos[i];
for(int i=1;i<=y.len;i++) rs.pos[i+len]=y.pos[i];
sort(rs.pos+1,rs.pos+rs.len+1);
for(int i=1;i<=rs.len;i++) for(int j=i;j<=rs.len;j++)
rs.id[i][j]=mrg(qryid(rs.pos[i],rs.pos[j]),y.qryid(rs.pos[i],rs.pos[j]));
return rs;
}
inline void output()
{
puts("-----------------------------------------");
printf("seg : %d\nthe val : ",len);
for(int i=1;i<=len;i++) printf("%d%c",pos[i],i==len?'\n':' ');
for(int i=1;i<=len;i++) for(int j=i;j<=len;j++) printf("%d%c",id[i][j],j==len?'\n':' ');
puts("-----------------------------------------");
}
}A[N/B+5];
inline block solve(int l,int r)
{//值域区间
int md=(l+r)>>1;if(l==r) return block(l);
return solve(l,md)+solve(md+1,r);
}
int main()
{
read(n,q),rst=n;for(int i=1;i<=n;i++) read(a[i]),wh[a[i]]=i;
for(int i=0;i<=(n-1)/B;i++) A[i]=solve(i*B+1,min(i*B+B,n));
for(int k=1,l,r;k<=q;k++)
{
read(l,r),rsid[k]=0;
for(int i=0;i<=(n-1)/B;i++) rsid[k]=mrg(rsid[k],A[i].qryid(l,r));
}
printf("%d\n",rst);for(int i=n+1;i<=rst;i++) printf("%d %d\n",rsx[i],rsy[i]);
for(int i=1;i<=q;i++) printf("%d%c",rsid[i],i==q?'\n':' ');
return 0;
}