1. 程式人生 > >【CF765F】Souvenirs 主席樹

【CF765F】Souvenirs 主席樹

tran min log 復雜 OS brush val ron 滿足

【CF765F】Souvenirs

題意:給你一個長度為n的序列{ai},有m個詢問,每次詢問給出l,r,問在所有$l\le x < y\le r$中,$|a_x-a_y|$的最小值是多少。

$n\le 10^5,m\le 3\times 10^5,a_i\le 10^9$

題解:網上的標程都是在線段樹上搞一搞就完事了,但是我怎麽看都覺得是$O(n\log^3n)$的。看官方題解,裏面也沒寫具體做法。於是我一臉懵逼的情況下用了主席樹來維護,起碼保證了$O(n\log^2n)$的復雜度。(應該是做麻煩了)

說做法吧。我們先只考慮$j<i,a_j>a_i$的情況,然後反過來再做一遍。首先比較暴力的思路就是先將詢問離線,按右端點排序,然後枚舉右端點。當我們掃到一個右端點i的時候,先找到i左邊第一個比它大的數j,用$a_j-a_i$更新j左邊的答案,然後不斷找到j左邊,比$a_j$小,比$a_i$大的j‘,重復此步驟做下去。

而一個比較重要的性質就是我們找到的j‘只有在滿足$a_{j‘}-a_i<a_j-a_{j‘}$的情況下才是有意義的(否則就用$a_j-a_{j‘}$做答案了),所以每次我們的差都會減半,所需次數為log次。如果用樹狀數組更新答案的話復雜度就是$O(n\log n\log a_i)$的了。

但是怎麽找到下一個$j‘$呢?我比較菜所以用的主席樹。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int n,m,N,top,pre,tot;
const int maxn=100010;
int v[maxn],st[maxn],sn[maxn],ans[maxn*3],p[maxn],rk[maxn],ref[maxn],rt[maxn];
struct node
{
	int x,org;
	node() {}
	node(int a,int b) {x=a,org=b;}
};
vector<node> q[maxn];
vector<node>::iterator it;
int val[maxn<<2];
struct sag
{
	int ls,rs,siz;
}s[maxn*20];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
bool cmp(const int &a,const int &b) {return v[a]<v[b];}
inline void updata(int x,int val)
{
	for(int i=x;i;i-=i&-i)	sn[i]=min(sn[i],val);
}
inline int query(int x)
{
	int ret=1<<30;
	for(int i=x;i<=n;i+=i&-i)	ret=min(ret,sn[i]);
	return ret;
}
inline void insert(int x,int &y,int l,int r,int a)
{
	y=++tot,s[y].ls=s[x].ls,s[y].rs=s[x].rs,s[y].siz=s[x].siz+1;
	if(l==r)	return ;
	int mid=(l+r)>>1;
	if(a<=mid)	insert(s[x].ls,s[y].ls,l,mid,a);
	else	insert(s[x].rs,s[y].rs,mid+1,r,a);
}
inline int getrank(int x,int y,int l,int r,int a)
{
	if(l==r)	return s[y].siz-s[x].siz;
	int mid=(l+r)>>1;
	if(a<=mid)	return getrank(s[x].ls,s[y].ls,l,mid,a);
	return s[s[y].ls].siz-s[s[x].ls].siz+getrank(s[x].rs,s[y].rs,mid+1,r,a);
}
inline int find(int x,int y,int l,int r,int a)
{
	if(l==r)	return l;
	int mid=(l+r)>>1,t=s[s[y].ls].siz-s[s[x].ls].siz;
	if(a<=t)	return find(s[x].ls,s[y].ls,l,mid,a);
	return find(s[x].rs,s[y].rs,mid+1,r,a-t);
}
void work()
{
	int i,j,t,last;
	for(i=1;i<=n;i++)	p[i]=i;
	sort(p+1,p+n+1,cmp);
	for(N=0,i=1;i<=n;i++)
	{
		if(i==1||v[p[i]]>v[p[i-1]])	ref[++N]=v[p[i]];
		rk[p[i]]=N;
	}
	ref[0]=-1<<30,ref[N+1]=1<<30;
	tot=0;
	for(i=1;i<=n;i++)	insert(rt[rk[p[i-1]]],rt[rk[p[i]]],1,n,p[i]);
	memset(sn,0x3f,sizeof(sn));
	for(st[top=0]=0,i=1;i<=n;i++)
	{
		while(top&&rk[st[top]]<rk[i])	top--;
		last=st[top];
		while(last)
		{
			updata(last,v[last]-v[i]);
			if(rk[last]==rk[i])	break;
			pre=0;
			j=upper_bound(ref+1,ref+N+1,(v[last]+v[i])>>1)-ref-1;
			if(ref[j]<v[i])	break;
			t=getrank(rt[rk[i]-1],rt[j],1,n,i);
			if(t==1)	break;
			last=find(rt[rk[i]-1],rt[j],1,n,t-1);
		}
		for(it=q[i].begin();it!=q[i].end();it++)	ans[(*it).org]=min(ans[(*it).org],query((*it).x));
		st[++top]=i;
	}
}
int main()
{
	n=rd();
	int i,a,b;
	for(i=1;i<=n;i++)	v[i]=rd();
	m=rd();
	memset(ans,0x3f,sizeof(ans));
	for(i=1;i<=m;i++)	a=rd(),b=rd(),q[b].push_back(node(a,i));
	work();
	for(i=1;i<=n;i++)	v[i]=-v[i];
	work();
	for(i=1;i<=m;i++)	printf("%d\n",ans[i]);
	return 0;
}//3 1 1 1 1 1 2

【CF765F】Souvenirs 主席樹