「 题解 」P2487 [SDOI2011]拦截导弹
简单题意
给定 \(n\) 个数对 \((h_i, v_i)\)。
求:
- 最长不上升子序列的长度。
- 对于每个 \(i\),分别求出包含数对 \((h_i, v_i)\) 的最长上升子序列的个数和最长不上升子序列的个数和的比。
数据范围:\(1 \leq n \leq 5 \times {10} ^ 4\),\(\forall 1 \leq i \leq n, 1 \leq h_i, v_i \leq {10}^9\)。
分析
问题 \(1\)
先考虑 \(O(n^2)\) 做法,本质与一维的相同。
定义 \(f_i\) 表示以数对 \((h_i, v_i)\) 结尾的最长不上升子序列的长度。
那么有
f_i & = \max(1, \max\{ f_j + 1 \}), h_i \leq h_j \wedge v_i \leq v_j \\
\end{aligned}
\]
推出了式子后考虑优化,可以使用 \(\texttt{cdq}\) 分治。
对于一个区间 \(\texttt{[l, r]}\) 的转移。
\(\texttt{int mid = (l + r) >> 1}\)
先递归 \(\texttt{cdq(l, mid)}\)。
假设 \(\texttt{[l, mid]}\) 已经求出正确答案,即 \(f_{\texttt{l} \sim \texttt{r}}\) 都是正确的。
考虑如何转移,即 \(\texttt{[l, mid]}\) 对 \(\texttt{[mid + 1, r]}\) 的贡献。
与三维偏序一样,合并 \(h\),以 \(v\) 为下标把 \(f\) 存放在树状数组中。
只不过这里的 \(f\) 需要取最大值。
最后递归 \(\texttt{cdq(mid + 1, r)}\)。
因为是最后递归 \(\texttt{cdq(mid + 1, r)}\),所以询问不能真正合并,在结束时需要还原成原来的顺序。
问题 \(2\)
问题 \(1\) 解决后问题 \(2\) 就简单了。
只需要在树状数组中再维护一个统计个数数组即可。
- 修改的值大于当前最大值就修改。
- 修改的值等于当前最大值就累加。
因为求的是 包含 数对 \((h_i, v_i)\) 的最长不上升子序列的个数,所以还需要反着求一遍最长不上升子序列的个数。
两个数相乘再除以总数就是答案,前提是这个数对被包含在至少一个最长不上升子序列中。
温馨提示,个数可能会超过 \(\texttt{long long}\) 的范围,建议使用 \(\texttt{double}\)。
\(\texttt{code}\)
#include <cstdio>
#include <vector>
#include <utility>
#include <iostream>
#include <algorithm>
int rint() {
int x = 0, fx = 1;
char c = getchar();
while (c < '0' || c > '9') {
fx ^= (c == '-');
c = getchar();
}
while ('0' <= c && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
if (!fx) {
return -x;
}
return x;
}
void read(int &x) {
x = rint();
}
template<typename... Ts>
void read(int &x, Ts &...rest) {
read(x);
read(rest...);
}
int Max(int u, int v) {
return (u > v) ? u : v;
}
int Min(int u, int v) {
return (u < v) ? u : v;
}
const int MAX_n = 5e4;
int n, Time; // Time 是时间戳优化树状数组,可以不用清空
int dp1[MAX_n + 5]; // 正
int dp2[MAX_n + 5]; // 反
int vis[MAX_n + 5]; // 树状数组时间戳
int Bit[MAX_n + 5];
double bit[MAX_n + 5];
double num1[MAX_n + 5]; // 正
double num2[MAX_n + 5]; // 反
std::vector<int> lsh; // 离散化 v
struct Missile {
int idx, h, v;
} q[MAX_n + 5];
Missile tmp[MAX_n + 5];
bool cmph1(Missile x, Missile y) {
return x.h > y.h;
}
bool cmph2(Missile x, Missile y) {
return x.h < y.h;
}
int lowbit(int x) {
return x & (-x);
}
void add(int k, int x, double y) {
while (k <= n) {
if (vis[k] != Time) {
vis[k] = Time;
Bit[k] = x;
bit[k] = y;
} else {
if (x > Bit[k]) {
Bit[k] = x;
bit[k] = y;
} else if (x == Bit[k]) {
bit[k] += y;
}
}
k += lowbit(k);
}
}
std::pair<int, double> ask(int k) {
int resmax = 0;
double ressum = 0.0;
while (k > 0) {
if (vis[k] == Time) {
if (Bit[k] > resmax) {
resmax = Bit[k];
ressum = bit[k];
} else if (Bit[k] == resmax) {
ressum += bit[k];
}
}
k -= lowbit(k);
}
return std::make_pair(resmax, ressum);
}
void merge1(int L1, int R1, int L2, int R2) {
++Time;
int i = L1, j = L2;
for (int k = L1; k <= R2; k++) {
tmp[k] = q[k];
}
std::sort(q + L1, q + R1 + 1, cmph1);
std::sort(q + L2, q + R2 + 1, cmph1);
while (i <= R1 || j <= R2) {
if (i <= R1 && (j > R2 || q[i].h >= q[j].h)) {
add((int)lsh.size() + 1 - q[i].v, dp1[q[i].idx], num1[q[i].idx]);
i++;
} else {
std::pair<int, double> now = ask((int)lsh.size() + 1 - q[j].v);
if (now.first + 1 > dp1[q[j].idx]) {
dp1[q[j].idx] = now.first + 1;
num1[q[j].idx] = now.second;
} else if (now.first + 1 == dp1[q[j].idx]) {
num1[q[j].idx] += now.second;
}
j++;
}
}
for (int k = L1; k <= R2; k++) {
q[k] = tmp[k];
}
}
void cdq1(int L, int R) {
if (L == R) {
return ;
}
int Mid = (L + R) >> 1;
cdq1(L, Mid);
merge1(L, Mid, Mid + 1, R);
cdq1(Mid + 1, R);
}
void merge2(int L1, int R1, int L2, int R2) {
++Time;
int i = L1, j = L2;
for (int k = L1; k <= R2; k++) {
tmp[k] = q[k];
}
std::sort(q + L1, q + R1 + 1, cmph2);
std::sort(q + L2, q + R2 + 1, cmph2);
while (i <= R1 || j <= R2) {
if (i <= R1 && (j > R2 || q[i].h <= q[j].h)) {
add(q[i].v, dp2[q[i].idx], num2[q[i].idx]);
i++;
} else {
std::pair<int, double> now = ask(q[j].v);
if (now.first + 1 > dp2[q[j].idx]) {
dp2[q[j].idx] = now.first + 1;
num2[q[j].idx] = now.second;
} else if (now.first + 1 == dp2[q[j].idx]) {
num2[q[j].idx] += now.second;
}
j++;
}
}
for (int k = L1; k <= R2; k++) {
q[k] = tmp[k];
}
}
void cdq2(int L, int R) {
if (L == R) {
return ;
}
int Mid = (L + R) >> 1;
cdq2(L, Mid);
merge2(L, Mid, Mid + 1, R);
cdq2(Mid + 1, R);
}
signed main() {
n = rint();
for (int i = 1; i <= n; i++) {
read(q[i].h, q[i].v);
q[i].idx = i;
lsh.push_back(q[i].v);
}
std::sort(lsh.begin(), lsh.end());
lsh.resize(std::unique(lsh.begin(), lsh.end()) - lsh.begin());
for (int i = 1; i <= n; i++) {
q[i].v = std::lower_bound(lsh.begin(), lsh.end(), q[i].v) - lsh.begin() + 1;
dp1[i] = dp2[i] = 1;
num1[i] = num2[i] = 1.0;
}
cdq1(1, n);
for (int i = n / 2; i >= 1; i--) {
std::swap(q[i], q[n + 1 - i]);
}
cdq2(1, n);
int res = 0;
double sum = 0;
for (int i = 1; i <= n; i++) {
if (dp1[i] > res) {
res = dp1[i];
sum = num1[i];
} else if (dp1[i] == res) {
sum += num1[i];
}
}
printf("%d\n", res);
for (int i = 1; i <= n; i++) {
if (dp1[i] + dp2[i] - 1 != res) {
printf("0.00000");
} else {
printf("%.5f", 1.0 * num1[i] * num2[i] / sum);
}
putchar((i == n) ? '\n' : ' ');
}
return 0;
}
「 题解 」P2487 [SDOI2011]拦截导弹的更多相关文章
- P2487 [SDOI2011]拦截导弹
题目 P2487 [SDOI2011]拦截导弹 做\(SDOI\)有种想评黑的感觉,果然还是太弱了 做法 独立写(调)代码三个小时祭 简化题目:求二维最长不上升子序列及每个点出现在最长不上升子序列概率 ...
- bzoj 2244: [SDOI2011]拦截导弹 cdq分治
2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 237 Solved: ...
- 【BZOJ2244】[SDOI2011]拦截导弹(CDQ分治)
[BZOJ2244][SDOI2011]拦截导弹(CDQ分治) 题面 BZOJ 洛谷 题解 不难发现这就是一个三维偏序+\(LIS\)这样一个\(dp\). 那么第一问很好求,直接\(CDQ\)分治之 ...
- [BZOJ2244][SDOI2011]拦截导弹 CDQ分治
2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec Memory Limit: 512 MB Special Judge Description 某国为了防御敌国的导弹 ...
- 【LG2481】[SDOI2011]拦截导弹
[LG2481][SDOI2011]拦截导弹 题面 洛谷 题解 可以看出第一问就是一个有关偏序的\(LIS\),很显然可以用\(CDQ\)优化 关键在于第二问 概率\(P_i=\) \(总LIS数\) ...
- BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治
2244: [SDOI2011]拦截导弹 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截 ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
随机推荐
- 基于机器学习和TFIDF的情感分类算法,详解自然语言处理
摘要:这篇文章将详细讲解自然语言处理过程,基于机器学习和TFIDF的情感分类算法,并进行了各种分类算法(SVM.RF.LR.Boosting)对比 本文分享自华为云社区<[Python人工智能] ...
- 计算机网络-4-11-IP多播
IP多播 IP多播的基本概念 与单播相比,在一对多的通信中,多播可以大大减少网络资源.在互联网上进行多播就叫做IP多播,IP多播所传送的分组需要使用多播IP地址.能够运行多播协议的路由器叫做多播路由器 ...
- mysql数据库主从复制教程
mysql主从复制教程 架构规划: 192.168.201.150 master 主节点 192.168.201.154 slave 从节点 1. 修改mysql的配置文件(主节点,从节点都要修改) ...
- antd递归渲染左侧菜单
- 日志收集系统系列(三)之LogAgent
一.什么是LogAhent 类似于在linux下通过tail的方法读日志文件,将读取的内容发给kafka,这里的tailf是可以动态变化的,当配置文件发生变化时,可以通知我们程序自动增加需要增加的配置 ...
- Standalone集群搭建和Spark应用监控
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6815920501530034696/ 承接上一篇文档<Spark词频前十的统计练习> Spark on ...
- 获取iframe外的document
在iframe中点击弹出层外部分弹出层消失,但是点击iframe外部分就操作不了弹出层了,被这个问题困扰了不少时间,今天得以解决,代码如下: 说明:$(top.document,document).c ...
- 移动端position:fixed 解决方案
相信不少人做移动端项目的时候都会遇到position:fixed 的坑. 下面提供一个解决方法,不用引入任何其他的js库,纯css解决. 解决问题的关键就是:fixed元素内部必须嵌套一个positi ...
- 极客大挑战2019 http
极客大挑战 http referer 请求头 xff 1.查看源码,发现secret.php 2.提示要把来源改成Sycsecret.buuoj.cn,抓包,添加Referer Referer:htt ...
- winform设置所有窗体统一图标
class WindowHookerManager { static WindowHooker hooker = new WindowHooker(); public static void SetA ...