Set Merging[CF1375H][分块+权值线段树]

文章目录

题目

Luogu
在这里插入图片描述

思路

n ≤ 4096 n\le 4096 n4096
发现对于合并时候权值限制比较强然后放在值域上搞,
那么合并相当于值域上分两边选合起来
那么原序列 [ 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)=2T(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值