【Codeforces 611G】New Year and Cake

计算几何,双指针

题目大意:

给定一个凸 $n$ 边形,它的对角线有 $\frac{n(n-3)}2$ 条。

对每条对角线,会将凸 $n$ 边形分成两部分,这条对角线的贡献为分成的两部分中,面积较大的那部分的面积减去另一部分面积。

求所有的对角线的贡献之和的两倍。

题解:

令凸包面积为 $S$,对每条对角线,其分成的两部分面积设为 $A,S-A$。

令 $S-A\geq A$,则该对角线的贡献为 $S-2A$。

$S$ 是不变的,所以我们只需要求出 所有对角线的 $A$ 的和。

考虑双指针,固定一个端点,并让另一个端点尽可能往后移动。保证这两个点构成的半平面截得的凸包部分的面积始终不超过 $\frac S 2$。

用向量叉积计算面积。

这部分比较好处理,还有就是第一个端点移动时带来的面积变化。

向量叉积满足分配律,所以记一下所有向量的和就可以计算了。

注意诸多爆 long long 的问题。

时间复杂度 $O(n)$。

Code:

#include<iostream>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
const int N=5e5+5,md=1e9+7;
struct point{
    LL x,y;
    inline point operator-(const point&rhs)const{return(point){x-rhs.x,y-rhs.y};}
    inline point operator+(const point&rhs)const{return(point){x+rhs.x,y+rhs.y};}
    inline point operator*(const int&a)const{return(point){x*a,y*a};}
}d[N];
uLL S;
int n,ans;
inline LL cross(const point&a,const point&b){return a.x*b.y-a.y*b.x;}
inline int crossMOD(const point&a,const point&b){return(a.x%md*(b.y%md)-a.y%md*(b.x%md)+(LL)md*md)%md;}
inline int NEXT(int x){return x%n+1;}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=n;i;--i)cin>>d[i].x>>d[i].y;
    for(int i=2;i<n;++i)S+=cross(d[i]-d[1],d[i+1]-d[1]);
    ans=S%md*(n*(n-3LL)/2%md)%md;
    d[n+1]=d[1],d[0]=d[n];
    uLL s=0;
    point vec=d[2];
    int ct=1,A=0;
    int cp=0;
    for(int i=1,it=2;i<=n;++i){
        while(s+cross(d[it]-d[i],d[NEXT(it)]-d[i])<=S/2){
            ++ct;
            s+=cross(d[it]-d[i],d[NEXT(it)]-d[i]);
            if(s*2==S)++cp;
            A=(A+s)%md;
            vec=vec+d[NEXT(it)];
            it=NEXT(it);
        }
        ans=(ans-(LL)(2*A%md)+md)%md;
        s-=cross(d[i+1]-d[i],d[it]-d[i]);
        A=(A-crossMOD(d[i+1]-d[i],vec-d[i]*ct)+md)%md;
        --ct;
        vec=vec-d[i+1];
    }
    ans=(ans+cp*(S/2%md))%md;
    cout<<ans<<'\n';
    return 0;
}