把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF611G】New Year and Cake(计算几何)

点此看题面

  • 给定一个\(n\)个点的凸多边形。
  • 求出多边形由每条对角线划分成的两部分面积之差的和。
  • \(n\le5\times10^5\)

双指针判正负贡献

考虑双指针,可以维护出从一个顶点出发,哪一半左边面积大,哪一半右边面积大。

方便起见,我们只求出较小部分面积的贡献,结束时用所有情况下的总面积之和减去两倍该贡献即可得出答案。

由于实际实现中两半面积相同时候可能会出现一些问题,因此特殊处理该部分,不把此时的面积计入贡献中,记录这种情况的个数并在最后减去。

在切换顶点的时候,如果直接记录面积是很难维护出面积的变化量的,因此利用叉积的分配律,维护好贡献向量之和,并在切换顶点的时候更新即可。

注意取模的问题,一般向量的面积需要比大小,必须开\(unsigned\ long\ long\)而不能取模;而贡献向量的和向量是用于计算贡献的,并不会被用于比大小,且显然和其他向量相叉可能会达到\(2\times10^{24}\)级别,因此必须取模。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define X 1000000007
#define ull unsigned long long
using namespace std;
int n;struct P
{
	int x,y;I P(CI a=0,CI b=0):x(a),y(b){}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
}p[2*N+5];
struct MP
{
	int x,y;I MP(CI a=0,CI b=0):x(a),y(b){}I MP(Con P& p):x((p.x%X+X)%X),y((p.y%X+X)%X){} 
	I void operator += (Con MP& o) {x=(x+o.x)%X,y=(y+o.y)%X;}
	I void operator -= (Con MP& o) {x=(x-o.x+X)%X,y=(y-o.y+X)%X;}
	I MP operator * (CI o) Con {return MP(1LL*x*o%X,1LL*y*o%X);}
};
I ull S (Con P& A,Con P& B) {return abs(1LL*A.x*B.y-1LL*A.y*B.x);}//用于比大小的面积
I int S (Con MP& A,Con MP& B) {return (1LL*A.x*B.y-1LL*A.y*B.x%X+X)%X;}//用于计算贡献的面积
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define D isdigit(oc=tc())
	int Ff;char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0,Ff=1;W(!D) Ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),D);x*=Ff;}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int main()
{
	RI i,j;for(read(n),i=1;i<=n;++i) read(p[i].x,p[i].y),p[n+i]=p[i];
	ull s=0;for(i=2;i^n;++i) s+=S(p[i]-p[1],p[i+1]-p[1]);//计算总面积
	RI c=0,w=0,t=0;ull f=0,g;MP sp;for(i=j=1;i<=n;++i)//枚举顶点
	{
		W(1) if(2*(f+(g=S(p[j]-p[i],p[j+1]-p[i])))<s) w=(w+(f+=g))%X,sp+=p[++j]-p[i];else {2*(f+g)==s&&++c;break;}//双指针移动
		t=(t+w)%X,f-=S(p[j]-p[i],p[j]-p[i+1]),w=(w-S(sp,p[i+1]-p[i])+X)%X,sp-=MP(p[i+1]-p[i])*(j-i);//计算贡献,然后切换端点
	}return printf("%d\n",((1LL*n*(n-3)-c)/2%X*(s%X)-2*t+2*X)%X),0;//总情况数下的面积总和减去求出贡献的两倍
}
posted @ 2021-04-06 10:31  TheLostWeak  阅读(60)  评论(0编辑  收藏  举报