P5302 [GXOI/GZOI2019]特技飞行
题目地址:P5302 [GXOI/GZOI2019]特技飞行
这里是官方题解(by lydrainbowcat)
题意
给 \(10^5\) 条直线,给 \(x = st\) 和 \(x = ed\) 两个位置
在两条直线 \(l1,l2\) 交点,可以交换 \(l1,l2\) 接下来的部分(变成两条折线)
交换或不交换分别可以获得固定的分数 \(a\) 和 \(b\)
另外有 \(10^5\) 个观测点可以观测到一定范围内情况(曼哈顿距离),在观测范围内的点额外计分 \(c\)
要求最后在 \(x = st\) 处和 \(x = ed\) 处,直线保持相同的顺序
问如何交换可以获得最高的得分。保证交点小于等于 \(5*10^5\) 个
解法
这其实是两个题的拼接
首先,若 \(a>b\) ,说明交换越多越好。实际上所有交点都可以交换,因为交点个数恰好是逆序对数,所有逆序对都交换一下最后正好变成正序
若 \(a<b\) ,交换次数为 $n - $ 置换数,因为每个置换之间的互相独立的,可以不参与交换。每个置换内部其实都等价于一个环(5-1-2-3-4这样的),交换次数为 \(len-1\) ,故总次数为 $\sum (len - 1) = n - $ 置换数。
所有交点可以用类似于排序的方法 \(O(n)\) 预处理,坐标转 \(45\) 度
接下来就是若干个点(事件),还有若干个正方形(拆成两个事件)的扫描线问题
扫描线 + 树状数组即可
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define MAX_NR 100010
const long double eps = 1e-9;
int N, A, B, C, ST_X, ED_X, M;
struct Point {
long double x, y;
Point() = default;
Point(int x_, int y_) :
x(x_), y(y_) {}
Point(long double x_, long double y_) :
x(x_), y(y_) {}
} point[MAX_NR * 2];
struct Event {
long double x;
int y0, y1, op;
Event() = default;
Event(int x_, int y0_, int y1_, int op_) :
x(x_), y0(y0_), y1(y1_), op(op_) {}
Event(long double x_, int y_) :
x(x_), y0(y_), y1(y_), op(0) {}
inline bool operator < (const Event &a) const {
if (fabs(x - a.x) > eps) {
return x < a.x;
} else if (op != a.op) {
return op > a.op;
} else {
return false;
}
}
};
std::vector<Point> inter;
inline Point get_intersection(Point a, Point b, Point c, Point d) {
long double a1 = b.y - a.y;
long double b1 = a.x - b.x;
long double c1 = a1 * a.x + b1 * a.y;
long double a2 = d.y - c.y;
long double b2 = c.x - d.x;
long double c2 = a2 * c.x + b2 * c.y;
long double determinant = a1 * b2 - a2 * b1;
if (determinant == 0) {
return { -1, -1 };
} else {
long double x = (b2*c1 - b1 * c2) / determinant;
long double y = (a1*c2 - a2 * c1) / determinant;
return { x, y };
}
}
namespace BIT {
int N;
int b[MAX_NR * 20];
void init(int n) {
N = n;
memset(b, 0, sizeof(b));
}
int query(int x) {
int res = 0;
for (int i = x; i > 0; i -= i & (-i)) {
res += b[i];
}
return res;
}
void update(int x, int v) {
for (int i = x; i <= N; i += i & (-i)) {
b[i] += v;
}
}
}
bool cmp(int i,int j) {
return point[N + i].y < point[N + j].y;
}
int main() {
scanf("%d%d%d%d%d%d", &N, &A, &B, &C, &ST_X, &ED_X);
for (int i = 0, x, y; i < N * 2; ++i) {
scanf("%d", &y);
x = (i < N ? ST_X : ED_X);
point[i] = { x, y };
}
std::vector<int> p(N), rank(N), a(N), v(N);
for (int i = 0; i < N; ++i) {
a[i] = rank[i] = i;
v[i] = 0;
}
std::sort(rank.begin(), rank.end(), cmp);
for (int i = 0; i < N; ++i) {
p[rank[i]] = i;
}
int nr_cross = N;
for (int i = 0; i < N; ++i) {
if (v[i]) continue;
nr_cross--;
int x = p[i];
while (x != i) {
v[x] = 1;
x = p[x];
}
}
for (int i = 0; i < N; ++i) {
for (int j = i; ; ++j) {
if (i == p[j]) {
int aj = a[j];
for (int k = j - 1; k >= i; --k) {
Point cur = get_intersection(
point[a[k]], point[a[k] + N],
point[aj], point[aj + N]);
inter.push_back(Point(cur.x + cur.y, cur.x - cur.y));
std::swap(p[k], p[k + 1]);
a[k + 1] = a[k];
}
break;
}
}
}
scanf("%d", &M);
std::vector<long double> Y(M * 2 + inter.size());
std::vector<Event> events(M * 2 + inter.size());
for (int i = 0, x, y, r; i < M; ++i) {
scanf("%d%d%d", &x, &y, &r);
int x_ = x, y_ = y;
x = x + y, y = x_ - y_;
events[i] = { x - r, y - r, y + r, 1 };
events[i + M] = { x + r, y - r, y + r, -1 };
Y[i] = y - r;
Y[i + M] = y + r;
}
for (int i = 0; i < inter.size(); ++i) {
Y[2 * M + i] = inter[i].y;
}
std::sort(Y.begin(), Y.end());
// Y.resize(std::unique(Y.begin(), Y.end()) - Y.begin());
int pos = 0;
for (int i = 0; i < Y.size(); i++)
if (i == 0 || fabs(Y[i] - Y[i - 1]) > eps) Y[pos++] = Y[i];
Y.resize(pos);
for (int i = 0; i < 2 * M; ++i) {
events[i].y0 = std::lower_bound(Y.begin(), Y.end(), events[i].y0) - Y.begin() + 1;
events[i].y1 = std::lower_bound(Y.begin(), Y.end(), events[i].y1) - Y.begin() + 1;
}
for (int i = 0; i < inter.size(); ++i) {
int y = std::lower_bound(Y.begin(), Y.end(), inter[i].y) - Y.begin() + 1;
events[2 * M + i] = { inter[i].x, y };
}
std::sort(events.begin(), events.end());
int nr_observed = 0;
BIT::init(2 * Y.size());
for (int i = 0; i < events.size(); i++) {
Event e = events[i];
if (e.op) {
BIT::update(e.y0, e.op);
BIT::update(e.y1 + 1, -e.op);
} else {
nr_observed += BIT::query(e.y0) > 0;
}
}
int nr_inv = inter.size();
int score_observed = nr_observed * C;
int max_score = A * nr_inv;
int min_score = A * nr_cross + B * (nr_inv - nr_cross);
if (A < B) {
std::swap(min_score, max_score);
}
std::cout << min_score + score_observed << ' ' << max_score + score_observed << std::endl;
return 0;
}
P5302 [GXOI/GZOI2019]特技飞行的更多相关文章
- luogu P5302 [GXOI/GZOI2019]特技飞行
传送门 强行二合一可还行 首先\(c\)的贡献是不会变的,先考虑求出多少交点被矩形覆盖,交点的话可以按左端点纵坐标从下到上顺序枚举一条线段,然后维护右端点纵坐标的set,把之前处理过线段的右端点放进s ...
- 题解-GXOI/GZOI2019 特技飞行
Problem loj3085 bzoj不放题面差评 题意概要:给出两条竖直直线,再给出 \(n\) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上.现要求安排所有飞机在航线相 ...
- [GXOI/GZOI2019]特技飞行
题目链接 [https://www.luogu.org/problem/P5302] 思路:这道题可以说是两道题的合并.注意到\(c\)的分数与 \(a\)和\(b\)的分数 无关,也就是说可以分成两 ...
- [GX/GZOI2019]特技飞行(扫描线+置换)
感觉是6题中最难的一题,其实这题是一个二合一: 第一问:给定平面上若干点和k个关键点,关键点覆盖一个45°倾斜的正方形范围r,求有多少点被至少一个关键点覆盖.这个可以曼哈顿转切比雪夫距离,然后再扫描线 ...
- GXOI/GZOI2019题解
GXOI/GZOI2019题解 P5300 [GXOI/GZOI2019]与或和 一眼题.. 显然枚举每个二进制位,答案就变成了全1子矩阵数量. 这个xjb推推,单调栈一下就行了. #include& ...
- Loj #3085. 「GXOI / GZOI2019」特技飞行
Loj #3085. 「GXOI / GZOI2019」特技飞行 题目描述 公元 \(9012\) 年,Z 市的航空基地计划举行一场特技飞行表演.表演的场地可以看作一个二维平面直角坐标系,其中横坐标代 ...
- 【LOJ】#3085. 「GXOI / GZOI2019」特技飞行
LOJ#3085. 「GXOI / GZOI2019」特技飞行 这显然是两道题,求\(C\)是一个曼哈顿转切比雪夫后的线段树扫描线 求\(AB\),对向交换最大化和擦身而过最大化一定分别为最大值和最小 ...
- 「GXOI / GZOI2019」简要题解
「GXOI / GZOI2019」简要题解 LOJ#3083. 「GXOI / GZOI2019」与或和 https://loj.ac/problem/3083 题意:求一个矩阵的所有子矩阵的与和 和 ...
- BZOJ2697: 特技飞行
2697: 特技飞行 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 607 Solved: 363[Submit][Status] Descript ...
随机推荐
- .NET和PHP程序员如何通过技术快速变现
刚开始写博客不足之处望大家多多指点,少一些质疑多一些帮助,我们就能成为朋友. 上一篇:<.NET程序员我是如何通过一个产品在2年内买车买房>有很多同为程序员的小伙伴们给我留言,从整体的留言 ...
- zcu102 hdmi example(二)
1.概述 上篇说到,调用跑HDMI IP核自带的design example,跑出来的结果是显示屏显示彩条,并伴有嘀,嘀,嘀...的声音.因为在实际项目中,我们只需要图像,不需要声音的,所以我要把声音 ...
- Python的生成器send()方法 & yield_from
生成器对象是一个迭代器.但是它比迭代器对象多了一些方法,它们包括send方法,throw方法和close方法.这些方法,主要是用于外部与生成器对象的交互.本文先介绍send方法. send send方 ...
- WebApi(五)-Swagger接口文档①简单集成
1,通过NuGet引用Swashbuckle 2,打开项目属性-->生成,勾选XML文档文件,保存 3,找到项目App_Start文件夹下WebApiConfig查找GetXmlComments ...
- 一入OI深似海 3 —— 纪念我最后一次PJ(上)
其实在比赛前一天中午上车前, 我还在机房打 I wanna, 感觉就是去杭州旅游的. 诶,还真是这样! 我和jwj在绍兴服务区买了金拱门, 拎着吃的回到车上的时候, 迎面而来羡慕的小眼神. 下午很早就 ...
- Python scrapy爬虫数据保存到MySQL数据库
除将爬取到的信息写入文件中之外,程序也可通过修改 Pipeline 文件将数据保存到数据库中.为了使用数据库来保存爬取到的信息,在 MySQL 的 python 数据库中执行如下 SQL 语句来创建 ...
- rk3128 手动挂载 U 盘
2019-04-16 关键字: RK . 挂载.U盘 笔者手里有一块非常原生的运行 Android 4.4 操作系统的 RK3128 开发板.原生到各种功能模块都不能用的地步.今天就遇到一个不按常理出 ...
- Python 编写一个有道翻译的 workflow 教程
最近使用有道翻译的 workflow 总是翻译不了,可能是 appKey 失效了或者超过调用上限,所以打算自己实现一个. 创建 workflow 打开 Alfred3 的 Preferences,选择 ...
- 使用logstash同步MySQL数据到ES
使用logstash同步MySQL数据到ES 版权声明:[分享也是一种提高]个人转载请在正文开头明显位置注明出处,未经作者同意禁止企业/组织转载,禁止私自更改原文,禁止用于商业目的. https:// ...
- pwn-ROP(2)
通过int80系统只对静态编译有效,动态编译需要用其他方法 本题提供了一个地址输入端,输入函数地址会返回该函数的实际地址,我们用得到的实际地址-偏移地址=基地址,然后用基地址+任意函数的偏移地址就可以 ...