题意:有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]);
}