XXVII.CF611G New Year and Cake
做题时居然忘记了叉积满足分配律/jk
我们先将图形翻转成为逆时针排布。
首先,我们发现,若总图形的面积是 \(area\),切完后,较小一半的面积是 \(nowarea\),则贡献是 \(area-2nowarea\)。
我们记点 \(p_i,p_{i+1},\dots,p_j\) 构成的图形为 \(\text{多边形}i\sim j\)
于是我们对于每个 \(p_i\),考虑求出其后方最后一个 \(p_j\),使得 \(S_{\text{多边形}i\sim j}\leq\dfrac{area}{2}\)。(当然,这里的 \(p_j\) 是循环意义下的)。按照套路,这应该用two-pointers就能轻松解决。
然后,对于每一个 \(i+2\leq k\leq j\),\(S_{\text{多边形}i\sim k}\) 都能作为 \(nowarea\) 被计算。
于是我们现在要求出 \(\sum\limits_{k=i+2}^jarea-2S_{\text{多边形}i\sim k}\)。拆开,得到 \((j-i-1)area-2\sum\limits_{k=i+2}^jS_{\text{多边形}i\sim k}\)。
前一半很好算,关键是后一半。随着右边界 \(j\) 的增加,此和是很好维护的;但是,当左边界 \(i\) 增加时,和就不太好维护了。
我们考虑,当 \(i\) 增加 \(1\) 时,\(S_{\text{多边形}i\sim k}\),减少 \(S_{\triangle p_ip_{i+1}p_k}\)。其可被表示成 \((p_{i+1}-p_i)\times(p_k-p_i)\)。
考虑不同的 \(p_k\),因为叉积满足分配律,所以当 \(i\) 增加 \(1\) 时,后面的东西减少了
再拆,得到
明显此处 \(\Sigma\) 内的东西就很好维护了,于是我们原本想维护的那一大坨也就可以维护了。
复杂度 \(O(n)\)。
(注意,当比较面积是否超过 \(area/2\) 时,此处的面积不能取模;然而,其它时候(特别是维护 \(\sum p_k\) 的时候),模是一定要取的,所以解决方案是写一套自动取模的向量模板,再写一套不取模的)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod=1e9+7;
int n,res;
struct MV{//moduloed vector
int x,y;
MV(){x=y=0;}
MV(int X,int Y){x=(X+mod)%mod,y=(Y+mod)%mod;}
friend MV operator +(const MV &u,const MV &v){return MV((u.x+v.x)%mod,(u.y+v.y)%mod);}
friend MV operator -(const MV &u,const MV &v){return MV((u.x+mod-v.x)%mod,(u.y+mod-v.y)%mod);}
friend MV operator *(const MV &u,const int &v){return MV(1ll*u.x*v%mod,1ll*u.y*v%mod);}
friend int operator &(const MV &u,const MV &v){return (1ll*u.x*v.y%mod-1ll*u.y*v.x%mod+mod)%mod;}//cross times
friend int operator |(const MV &u,const MV &v){return (1ll*u.x*v.x%mod+1ll*u.y*v.y%mod)%mod;}//point times
void operator +=(const MV &v){(x+=v.x)%=mod,(y+=v.y)%=mod;}
void operator -=(const MV &v){(x+=mod-v.x)%=mod,(y+=mod-v.y)%=mod;}
void read(){scanf("%d%d",&x,&y),(x+=mod)%=mod,(y+=mod)%=mod;}
void print(){printf("(%d,%d)",x,y);}
}q[500100];
struct OV{//ordinary vector
int x,y;
OV(){}
OV(int X,int Y){x=X,y=Y;}
friend OV operator +(const OV &u,const OV &v){return OV(u.x+v.x,u.y+v.y);}
friend OV operator -(const OV &u,const OV &v){return OV(u.x-v.x,u.y-v.y);}
friend ll operator &(const OV &u,const OV &v){return 1ll*u.x*v.y-1ll*u.y*v.x;}//cross times
friend ll operator |(const OV &u,const OV &v){return 1ll*u.x*v.x+1ll*u.y*v.y;}//point times
void read(){scanf("%d%d",&x,&y);}
void print(){printf("(%d,%d)",x,y);}
}p[500100];
ull area;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)p[n-i].read(),q[n-i]=MV(p[n-i].x,p[n-i].y);
for(int i=0;i<n;i++)area+=p[i]&p[(i+1)%n];
ull nowarea=0;
int sumarea=0;
MV v;
for(int i=0,j=0;i<n;i++){
while(((nowarea+((p[j]-p[i])&(p[(j+1)%n]-p[i])))<<1)<=area){
v+=q[(j+1)%n];
nowarea+=(p[j]-p[i])&(p[(j+1)%n]-p[i]);
(sumarea+=nowarea%mod)%=mod;
(++j)%=n;
}
(res+=(area%mod*((j-i+n-1)%n)%mod-2*sumarea%mod+mod)%mod)%=mod;
nowarea-=(p[(i+1)%n]-p[i])&(p[j]-p[i]),v-=q[(i+1)%n];
(sumarea+=mod-((q[(i+1)%n]-q[i])&(v-q[i]*((j-i+n-1)%n)))%mod)%=mod;
}
printf("%d\n",res);
return 0;
}