题解:P17369 [ECNA 2023] Convex Hull Extension
lailai0916 · · 题解
题意简述
给定一个按逆时针排列的严格凸整数多边形。统计可以加入的整数点,使新凸包保留全部原顶点、增加一个新顶点,且没有三个凸包顶点共线。若有无限多个答案,输出 infinitely many。
解题思路
加入新点后,原顶点的循环顺序不会改变。若全部原顶点都要保留,新点必然插入某一条原边的两个端点之间。因此,可以逐边统计该边能够插入的新点。
固定一条边
这三个条件也足够。新线段
不同原边对应的新点区域互不相交,因为新凸包中
接下来对当前边建立保持整数格点的坐标变换。平移使
把原坐标
变换矩阵的行列式为
设变换后的
记
区间宽度为
若
若
- 若
g\ge2 ,每一行至少包含一个整数,答案无限。 - 若
g=1 且b\mid a ,两个端点在每一行都是相邻整数,严格开区间内没有整数,这条边贡献0 。 - 若
g=1 且b\nmid a ,取k 不被b/\gcd(|a|,b) 整除时,左端点不是整数,长度为1 的开区间中恰有一个整数。这样的k 无限多,答案也无限。
若
分子减
对于一行
由于此时区间宽度为正,上式不会为负,无须再取最大值。它已经排除了三条边界上的全部整数点。
定义类欧几里得求和函数:
将
系数
为说明递推,设此时
第一行即使包含最上方额外的一层,其贡献也恰好为
代码中 exgcd 计算格点变换,floor_sum 计算 long long;求和中的大整商乘积及相减过程使用 __int128。
设坐标绝对值上界为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using i128=__int128;
const int N=55;
ll x[N],y[N];
tuple<ll,ll,ll> exgcd(ll a,ll b)
{
if(!b)return {a,1,0};
auto [g,x,y]=exgcd(b,a%b);
return {g,y,x-a/b*y};
}
i128 floor_sum(ll n,ll m,ll a,ll b)
{
ll q=a/m;
if(a%m<0)q--;
a-=q*m;
i128 res=(i128)q*n*(n-1)/2;
q=b/m;
if(b%m<0)q--;
b-=q*m;
res+=(i128)q*n;
while(1)
{
res+=(i128)(a/m)*n*(n-1)/2+(i128)(b/m)*n;
a%=m;
b%=m;
i128 y=(i128)a*n+b;
if(y<m)break;
n=y/m;
b=y%m;
swap(a,m);
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=0;i<n;i++)cin>>x[i]>>y[i];
ll ans=0;
for(int i=0;i<n;i++)
{
ll dx=x[(i+1)%n]-x[i],dy=y[(i+1)%n]-y[i];
auto [g,u,v]=exgcd(abs(dx),abs(dy));
if(dx<0)u=-u;
if(dy<0)v=-v;
int j=(i+n-1)%n,k=(i+2)%n;
ll a=-u*(x[j]-x[i])-v*(y[j]-y[i]),b=(dx*(y[j]-y[i])-dy*(x[j]-x[i]))/g,c=g-u*(x[k]-x[i])-v*(y[k]-y[i]),d=(dx*(y[k]-y[i])-dy*(x[k]-x[i]))/g;
ll det=a*d-c*b;
if(det<0||(det==0&&(g>1||a%b)))
{
cout<<"infinitely many"<<'\n';
return 0;
}
if(!det)continue;
ll m=(g*b*d-1)/det;
ans+=(ll)((i128)m*g+floor_sum(m,d,c,c-1)-floor_sum(m,b,a,a));
}
cout<<ans<<'\n';
return 0;
}