转载注明出处第七届蓝桥杯本科C/C++ B组 第五题 题解 广场舞(离散化) 可过100%测试数据_QNU_Yt的博客-CSDN博客
描述
LQ市的市民广场是一个多边形,广场上铺满了大理石的地板砖。
地板砖铺得方方正正,就像坐标轴纸一样。
以某四块砖相接的点为原点,地板砖的两条边为两个正方向,一块砖的边长为横纵坐标的单位长度,则所有横纵坐标都为整数的点都是四块砖的交点(如果在广场内)。
广场的砖单调无趣,却给跳广场舞的市民们提供了绝佳的参照物。每天傍晚,都会有大批市民前来跳舞。
舞者每次都会选一块完整的砖来跳舞,两个人不会选择同一块砖,如果一块砖在广场边上导致缺角或者边不完整,则没人会选这块砖。
(广场形状的例子参考【图1.png】)
现在,告诉你广场的形状,请帮LQ市的市长计算一下,同一时刻最多有多少市民可以在广场跳舞。
输入
输入的第一行包含一个整数n,表示广场是n边形的(因此有n个顶点)。
接下来n行,每行两个整数,依次表示n边形每个顶点的坐标(也就是说广场边缘拐弯的地方都在砖的顶角上。
数据保证广场是一个简单多边形。
输出
输出一个整数,表示最多有多少市民可以在广场跳舞。
输入样例
5
3 3
6 4
4 1
1 -1
0 4
输出样例
7
提示
【样例说明】
- 广场如图1.png所示,一共有7块完整的地板砖,因此最多能有7位市民一起跳舞。
【数据规模与约定】
- 对于30%的数据,n不超过100,横纵坐标的绝对值均不超过100。
- 对于50%的数据,n不超过1000,横纵坐标的绝对值均不超过1000。
- 对于100%的数据,n不超过1000,横纵坐标的绝对值均不超过100000000(一亿)。
【资源约定】
- 峰值内存消耗 < 256M
- CPU消耗 < 1000ms
【注意】
- 请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
- 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
- 注意: main函数需要返回0
- 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
- 注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
- 提交时,注意选择所期望的编译器类型。
关键位置都标明注释了,希望能帮到更多像我一样的初学者。(大佬勿喷)
话不多说,上代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
const double eps = 1e-8;
typedef long long LL;
int sgn(double x) {
if(fabs(x) < eps)
return 0;
if(x < 0)
return -1;
else
return 1;
}
struct Point {
LL x, y;
Point(LL x = 0, LL y = 0): x(x), y(y) {}
bool operator <(const Point &ano)const {
if(x == ano.x)
return y < ano.y;
return x < ano.x;
}
void input() {
scanf("%lld %lld", &x, &y);
}
} P[maxn];
struct Line {
Point s, e;
LL a, b, c; // 直线方程中 a/c 为斜率, b/c 为纵截距
bool k; // 可认为标志区间哪边矮
Line(Point s = Point(), Point e = Point()): s(s), e(e) {}
} L[maxn];
/**
在一段区间内,按线段中点纵坐标从小到大排序
*/
struct PosOfLine {
double midy;
int id;
PosOfLine(double midy = 0, int id = 0): midy(midy), id(id) {}
bool operator <(const PosOfLine &ano)const {
if(sgn(midy - ano.midy) == 0)
return id < ano.id;
return sgn(midy - ano.midy) < 0;
}
} pos[maxn]; /// 每个区间内都不会超过n条线段
int X[maxn];
int n;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
/**
计算 sum((LL)floor((a*i+b)*1.0/c),0<=i<n)
*/
LL count(LL a, LL b, LL c, LL n) {
if(a < 0)
return count(-a, b + a * (n - 1), c, n);
if(n == 0)
return 0;
LL ans = 0;
if(a >= c)
ans += n * (n - 1) / 2 * (a / c);
a %= c;
if((b < 0 ? -b : b) >= c)
ans += n * (b / c);
b %= c;
if(a == 0)
return ans;
ans += count(c, (a * n + b) % c, a, (a * n + b) / c);
return ans;
}
LL bub(Line l, int left, int right) {
LL ans = count(l.a, l.b, l.c, right + 1);
ans -= count(l.a, l.b, l.c, left);
return ans;
}
LL blb(Line l, int left, int right) {
LL ans = count(l.a, l.b + l.c - 1, l.c, right + 1);
ans -= count(l.a, l.b + l.c - 1, l.c, left);
return ans;
}
bool check(Line l0, Line l1, int left, int right) {
LL a1 = ((LL)l0.a * left + l0.b) / l0.c;
LL a2 = ((LL)l1.a * right + l1.b) / l1.c;
if(a1 <= a2)
return 1;
if(a1 - a2 > 1)
return 0;
int a3 = ((LL)l0.a * left + l0.b) % l0.c;
int a4 = ((LL)l1.a * right + l1.b) % l1.c;
return ((LL)a3 * l1.c - (LL)a4 * l0.c < 0);
}
LL solve(Line &up_line, Line &down_line, int left, int right) {
// 去掉左边上边界下方的方格数小于等于下边界下方的方格数的情况 , 说明没有完整的格子
if(check(up_line, down_line, left + up_line.k, left + !down_line.k)) {
int ll = left - 1, rr = right + 1;
while(ll + 1 < rr) {
int mid = (ll + rr) / 2;
if(check(up_line, down_line, mid + up_line.k, mid + !down_line.k))
ll = mid;
else
rr = mid;
}
left = rr;
if(left > right)
return 0;
}
// 去掉右边上边界下方的方格数小于等于下边界下方的方格数的情况 , 说明没有完整的格子
if(check(up_line, down_line, right - !up_line.k, right - down_line.k)) {
int ll = left - 1, rr = right + 1;
while(ll + 1 < rr) {
int mid = (ll + rr) / 2;
if(check(up_line, down_line, mid - !up_line.k, mid - down_line.k))
rr = mid;
else
ll = mid;
}
right = ll;
if(left > right)
return 0;
}
LL ans = 0;
// 计算上边界下方方格数
if(up_line.s.y < up_line.e.y)
ans += bub(up_line, left, right - 1); // 斜率大于0, 左边的格子矮,所以区间右端点减一
else
ans += bub(up_line, left + 1, right); // 斜率小于0, 右边的格子矮,所以区间左端点减一
// 计算下边界下方方格数
if(down_line.s.y < down_line.e.y)
ans -= blb(down_line, left + 1, right);
else
ans -= blb(down_line, left, right - 1);
return ans;
}
int main() {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
P[i].input();
X[i] = P[i].x;
}
for(int i = 0; i < n; ++i) {
L[i] = Line(P[i], P[(i + 1) % n]);
if(L[i].s.x > L[i].e.x)
swap(L[i].s, L[i].e);
LL t_a = L[i].e.y - L[i].s.y, t_b = L[i].e.x - L[i].s.x;
LL t_c = gcd(t_a, t_b);
t_b /= t_c;
t_a /= t_c;
if(t_b < 0) // t_b本来肯定为正,若现在为负说明t_c为负,那么t_a为负
t_b *= -1, t_a *= -1;
L[i].a = t_a;
L[i].c = t_b;
L[i].b = t_b * L[i].s.y - t_a * L[i].s.x;
L[i].k = L[i].s.y > L[i].e.y;
}
LL ans = 0;
sort(X, X + n); // 离散化
for(int i = 0; i < n - 1; ++i) { // 枚举所有段
if(X[i] ^ X[i + 1]) {
int cnt = 0;
for(int j = 0; j < n; ++j) { // 枚举多边形所有边
if(L[j].s.x <= X[i] && L[j].e.x >= X[i + 1]) { // 保证L[j]有一部分在区间[X[i], X[i+1]]
pos[cnt++] = PosOfLine((double)(L[j].a * (double)(X[i] + X[i + 1]) / 2.0 + L[j].b) / L[j].c, j);
}
}
sort(pos, pos + cnt); // cnt一定为偶数
for(int j = 0; j < cnt; j += 2) {
ans += solve(L[pos[j + 1].id], L[pos[j].id], X[i], X[i + 1]);;
}
}
}
cout << ans << endl;
return 0;
}