雅礼集训Day4T1convex(O(1)删除恢复)

题意:有n个点的凸包,现在给出这n个点的一个排列,m次询问一段区间的点构成的凸包面积。n,m<=150000

既然序列都给出来了,明显就是让你莫队。。。。。。

但是插入一个点的复杂度是O(logn)的。。。。。。没有信仰过不了。

考虑先把凸包建好,发现用数组表示双向链表的方法,删除一个点的复杂度为O(1)(凸包内没有别的点所以可以直接用双向链表维护)

可是莫队中不仅有删除还有插入,可以不插入吗?

当然不可以,但是我们可以把插入换一个名字:恢复。

当每次要回调的时候恢复就行了。但是为了保证恢复的正确性,对应的删除和恢复之间不能还有别的删除,是吗?

当然不是了,可以有别的删除,但是要像栈一样,后删除的先恢复,像括号一样套起来。

那怎么保证这个性质呢?

发现,在左端点移动时,i+1一定晚于i删除,早于i恢复,右端点移动时恰恰相反,

那么我们只要左右端点移动不交错就可以了,具体来说:

将序列分成sqrt(len)块,左端点在同一块的我们一起处理(左端点在同一块的按右端点从右到左排序),

在处理第i块时,将左端点放在第i块最左边,右端点放在序列最右边。

然后对于每个区间,先动之前的右端点(一定是从右到左)直到区间右端点,再将置于第i块最左边的左端点向右移。算出答案后,再将左端点移回第i块最左边,虽然看似有点浪费,但是可以保证总复杂度是O(n * sqrt(n)) 的。

AC代码:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define LL long long
#define S 400
#define maxn 150005
using namespace std;

int n,m,c[maxn],pre[maxn],suc[maxn];
bool usd[maxn];
inline void read(int &res)
{
	char ch;bool flag=0;
	for(;!isdigit(ch=getchar());) if(ch=='-') flag=1;
	for(res=ch-'0';isdigit(ch=getchar());res=10*res +ch -'0');
	(flag) && (res=-res);
}

struct Point 
{
	int x,y;
	Point(int a=0,int b=0):x(a),y(b){}
	LL operator *(const Point &B)const{ return 1ll * x * B.y - 1ll * y * B.x; }
	Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); } 
}P[maxn];

inline bool cmp1(const int &a,const int &b){ return P[a].x==P[b].x ? P[a].y < P[b].y : P[a].x < P[b].x ;}
int Q[maxn],L,R;
int l[maxn],r[maxn],sa[maxn],lb[maxn];
inline bool cmp2(const int &a,const int &b){ return lb[a]==lb[b] ? r[a] > r[b] : lb[a] < lb[b]; }

LL Ans,ans[maxn];

int main()
{
	
	freopen("convex.in","r",stdin);
	freopen("convex.out","w",stdout);
	
	read(n),read(m);
	for(int i=1;i<=n;i++) read(P[i].x),read(P[i].y),c[i]=i;
	sort(c+1,c+1+n,cmp1);
	for(int i=1;i<=n;Q[R++]=c[i],i++)
		for(;L<R-1 && (P[Q[R-1]] - P[Q[R-2]]) * (P[c[i]] - P[Q[R-2]]) < 0; R--);
	usd[Q[L]]=1;
	for(int i=L+1;i<R;i++)
		suc[pre[Q[i]]=Q[i-1]]=Q[i],usd[Q[i]]=1;
	int pr=Q[R-1];
	for(int i=n;i>=1;i--)
		if(!usd[c[i]])
			suc[pre[c[i]]=pr]=c[i],pr=c[i];
	suc[pre[Q[L]]=pr]=Q[L];
	for(int i=1;i<=n;i++) Ans+=abs(P[i] * P[suc[i]]);
	
	for(int i=1;i<=m;i++) read(l[i]),read(r[i]),sa[i]=i,lb[i]=(l[i]-1)/S;
	sort(sa+1,sa+1+m,cmp2);
	
	int ls=1,rs=n;
	for(int i=1;i<=m;i++)
	{
		
		int u=sa[i];
		if(lb[u]!=lb[sa[i-1]] && i!=1)
		{
			for(;rs<n;)
			{	
				rs++;
				int ed=suc[rs],st=pre[rs];
				suc[st] = rs;
				pre[ed] = rs;
				Ans+=P[pre[rs]] * P[rs] + P[rs] * P[suc[rs]] - P[pre[rs]] * P[suc[rs]];
			}
			for(;ls<lb[u] * S + 1;ls++)
			{
				pre[suc[ls]] = pre[ls] , suc[pre[ls]] = suc[ls];
				Ans+=P[pre[ls]] * P[suc[ls]]-P[pre[ls]] * P[ls] - P[ls] * P[suc[ls]];
			}
		}
		
		for(;rs>r[u];rs--) 
		{
			pre[suc[rs]] = pre[rs] , suc[pre[rs]] = suc[rs];
			Ans+=P[pre[rs]] * P[suc[rs]]-P[pre[rs]] * P[rs] - P[rs] * P[suc[rs]];
		}
		
		for(;ls<l[u];ls++)
		{
			pre[suc[ls]] = pre[ls] , suc[pre[ls]] = suc[ls];
			Ans+=P[pre[ls]] * P[suc[ls]]-P[pre[ls]] * P[ls] - P[ls] * P[suc[ls]];
		}
		
		ans[u]=Ans;
		
		for(;ls>lb[u]*S+1;)
		{
			ls--;
			int ed=suc[ls],st=pre[ls];
			suc[st] = ls;
			pre[ed] = ls;
			Ans+=P[pre[ls]] * P[ls] + P[ls] * P[suc[ls]] - P[pre[ls]] * P[suc[ls]];
		}
	}
	
	for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/80931059
今日推荐