Illuminations II(前缀和+分类讨论)

Illuminations II

[Link](Problem - F - Codeforces)

题意

给你一大一小两个凸多边形,保证小的多边形在大的里面。问随机站的大的多边形上一点,能看到小多变形的期望长度是多少。

题解

等价于求 ∑ 每 个 点 能 看 到 的 长 度 / 大 凸 多 边 形 的 周 长 \sum每个点能看到的长度 / 大凸多边形的周长 /,换一下等价于求 ∑ 小 凸 多 边 形 每 个 边 的 长 度 × 它 能 看 到 的 大 凸 多 形 的 长 度 / 大 凸 多 变 形 的 周 长 \sum小凸多边形每个边的长度\times 它能看到的大凸多形的长度/大凸多变形的周长 ×/。可以先预处理出大多边形的周长前缀和,然后枚举小多边形的每条边,找到他和大多边形的两个交点(旋转卡壳单调性),分类讨论一下对应的大凸多边形的长度,算一下贡献即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
double sum[N];
int main() {
    scanf("%d %d", &n, &m);
    vector<Point> sp(m + 1), bp(n + 1);
    for (int i = 0; i < n; i ++ ) {
        double x, y; scanf("%lf %lf", &x, &y);
        bp[i] = Point(x, y);
    }   
    for (int i = 0; i < m; i ++ ) {
        double x, y; scanf("%lf %lf", &x, &y);
        sp[i] = Point(x, y);
    }
  //  for (int i = 0; i < n; i ++ ) cout << bp[i].x << ' ' << bp[i].y << endl;
    vector<Line> sl(m + 1), bl(n + 1);
    for (int i = 0; i < n; i ++ ) {
        bl[i] = Line(bp[i], bp[(i + 1) % n]);
        if (i) sum[i] += sum[i - 1];
        sum[i] += bl[i].length();
    }
   // cout << sum[n - 1] << endl;
    for (int i = 0; i < m; i ++ ) sl[i] = Line(sp[i], sp[(i + 1) % m]);
    double res = 0;
    int a = 0, b = 0;
    double len = 0;
    for (int i = 0; i < m; i ++ ) {
        Line t = sl[i]; len = 0;
        while (!t.linecorssseg(bl[a]) || sgn((t.e- t.s) ^ (bl[a].e - bl[a].s)) < 0) a = (a + 1) % n;
        while (!t.linecorssseg(bl[b]) || sgn((t.e- t.s) ^ (bl[b].e - bl[b].s)) > 0) b = (b + 1) % n;
        Point pa = t.crossPoint(bl[a]), pb = t.crossPoint(bl[b]);
        //cout << a << ' ' << b << endl;
        len = pa.distance(bp[a]) + pb.distance(bp[(b + 1) % n]);
        if (a < b) { 
            if (a) len += sum[a - 1];
            len += sum[n - 1] - sum[b];
        }
        else {
            len += sum[a - 1] - sum[b];
        } 
        res += len * t.length();
    }
    printf("%.15lf\n", res / sum[n - 1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值