题解 「THUPC2022 初赛」 喵喵花園
发现若一个点确定了,整个 \(\tt k-gon\) 就确定了,可以三角形划分计算面积
然后这些点的值域较小似乎用处不大,那大概可以 rand 几个点或在以 \(\frac{C}{k}\) 为长度的一段上跑退火
然后神必题解说这是个单峰函数,鬼知道是为什么,所以可以三分
- 关于在类似正弦函数的函数上三分最值:
令周期为 \(T\),则以 \(\frac{T}{2}\) 为一段跑分段三分就行
保险起见可以让相邻两端稍有重叠,段长也可以稍小一些 - 关于计算凸多边形面积:
一个想法是三角形划分之后大力海伦公式,但精度比较感人
回忆向量叉积值的实际意义:两向量所夹平行四边形面积,除以 2 就是所夹三角形面积
于是拆分一下……图是盗的
那么多边形面积就是相邻顶点的向量表示的叉积之和除以 2 了
有趣的是,这一方法同样适用于凹多边形(因为叉积有正有负,多算的会被减掉)
并且这一方法并不要求所有向量都是用多边形上的点表示的
也就是说使用上图中的 \(\tt u, v, w\) 计算面积也是正确的,同样因为多算的会被减掉 - 关于极角排序:记得判断两向量共线时按长度排序
于是分段三分,注意卡一下精度就可以了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
// #define int long long
#define double long double
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
const double eps=1e-10;
double nxt[N], pre[N], c, step, len, ans=1e30;
struct point{
double x, y;
point() {x=y=0;}
point(double a, double b) {x=a; y=b;}
inline double abs() {return sqrt(x*x+y*y);}
inline double operator ^ (point b) {return x*b.y-b.x*y;}
inline point operator + (point b) {return point(x+b.x, y+b.y);}
inline point operator - (point b) {return point(x-b.x, y-b.y);}
inline bool operator < (point b) {double t=*this^b; if (t<eps) return abs()<b.abs(); else return t>0;}
inline point operator * (double t) {return point(x*t, y*t);}
}p[N], sta[N];
inline double dis(point a, point b) {return (a-b).abs();}
inline double area(point x, point y, point z) {
double a=dis(x, y), b=dis(y, z), c=dis(x, z), p=(a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
point getpos(int i, double dlt) {return dlt<eps?p[i]:p[i]+(p[(i+1)%n]-p[i])*(dlt/nxt[i]);}
double area() {
double ans=0;
// for (int i=3; i<=k; ++i) ans+=area(sta[1], sta[i-1], sta[i]);
// for (int i=1; i<k; ++i) sta[i]=sta[i]-sta[0];
// sta[0]={0, 0};
for (int i=0; i<k; ++i) ans+=sta[i]^sta[(i+1)%k];
return ans/2;
}
double calc(double mid) {
int now=0; double pre=mid;
for (int i=0; i<k; ++i,pre+=step) {
while (pre>=nxt[now]) pre-=nxt[now], now=(now+1)%n;
sta[i]=getpos(now, pre);
}
// for (int i=1; i<=k; ++i) cout<<"("<<sta[i].x<<','<<sta[i].y<<") "; cout<<endl;
return area();
}
signed main()
{
n=read(); k=read();
for (int i=0; i<n; ++i) p[i].x=read(), p[i].y=read();
// sort(p, p+n, [](point a, point b){return a.x==b.x?a.y<b.y:a.x<b.x;});
// for (int i=1; i<n; ++i) p[i]=p[i]-p[0];
// p[0]={0, 0};
// sort(p, p+n);
for (int i=0; i<n; ++i) c+=(nxt[i]=dis(p[i], p[(i+1)%n]));
// for (int i=0; i<n; ++i) cout<<"("<<p[i].x<<','<<p[i].y<<") "; cout<<endl;
// cout<<"nxt: "; for (int i=0; i<n; ++i) cout<<nxt[i]<<' '; cout<<endl;
step=c/k; len=step/100;
// cout<<"C: "<<c<<' '<<step<<endl;
// printf("%.10Lf\n", c);
for (double dlt=0; dlt<=step; dlt+=len) {
// cout<<"dlt: "<<dlt<<endl;
double l=dlt, r=dlt+len, lmid, rmid;
for (int i=1; i<=50; ++i) {
lmid=l+(r-l)/3, rmid=r-(r-l)/3;
if (calc(lmid)>calc(rmid)) l=lmid;
else r=rmid;
}
ans=min(ans, calc(l));
// cout<<"min: "<<l<<' '<<calc(l)<<endl;
}
printf("%.10Lf\n", ans);
return 0;
}