简单题意

给定 \(n\) 个数对 \((h_i, v_i)\)。

求:

  1. 最长不上升子序列的长度。
  2. 对于每个 \(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)\) 结尾的最长不上升子序列的长度。

那么有

\[\begin{aligned}
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]拦截导弹的更多相关文章

  1. P2487 [SDOI2011]拦截导弹

    题目 P2487 [SDOI2011]拦截导弹 做\(SDOI\)有种想评黑的感觉,果然还是太弱了 做法 独立写(调)代码三个小时祭 简化题目:求二维最长不上升子序列及每个点出现在最长不上升子序列概率 ...

  2. bzoj 2244: [SDOI2011]拦截导弹 cdq分治

    2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 237  Solved: ...

  3. 【BZOJ2244】[SDOI2011]拦截导弹(CDQ分治)

    [BZOJ2244][SDOI2011]拦截导弹(CDQ分治) 题面 BZOJ 洛谷 题解 不难发现这就是一个三维偏序+\(LIS\)这样一个\(dp\). 那么第一问很好求,直接\(CDQ\)分治之 ...

  4. [BZOJ2244][SDOI2011]拦截导弹 CDQ分治

    2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec  Memory Limit: 512 MB  Special Judge Description 某国为了防御敌国的导弹 ...

  5. 【LG2481】[SDOI2011]拦截导弹

    [LG2481][SDOI2011]拦截导弹 题面 洛谷 题解 可以看出第一问就是一个有关偏序的\(LIS\),很显然可以用\(CDQ\)优化 关键在于第二问 概率\(P_i=\) \(总LIS数\) ...

  6. BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治

    2244: [SDOI2011]拦截导弹 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截 ...

  7. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  8. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  9. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

随机推荐

  1. [学习笔记] IT项目管理 - 挣值管理(EVM)

    挣值管理(EVM) 挣值管理(Earned Value Management, EVM)是成本管理里面较为重点的知识点,但是也可以用来综合考察项目范围.进度和成本绩效,经常需要做相关计算. 基本概念P ...

  2. HiSql 实现case语法操作 新一代无实体ORM框架

    HiSql 实现case语法操作 在SqlServer,Oralce,Hana,PostGreSql,MySql 这些数据都支持SQL case语法,平常在实现业务开发中也会常用到,那么HiSql对于 ...

  3. RPC框架下实现文件上传到linux服务器

    一.前端使用element -ui 的upload组件 :data 表示额外传递的参数, :header 表示设置请求头参数,如,token等, action表示后台api接口的地址 二. conto ...

  4. 详解nginx的请求限制(连接限制和请求限制)

    https://www.jb51.net/article/143682.htm 一,背景 我们经常会遇到这种情况,服务器流量异常,负载过大等等.对于大流量恶意的攻击访问,会带来带宽的浪费,服务器压力, ...

  5. win11系统设置笔记本合盖上不休眠

    第一步.打开控制面板 第二步.打开控制面板 第三步.打开电源选项 第四步.如下图操作即可

  6. WebLogic任意文件上传漏洞(CVE-2019-2725)

    一,漏洞介绍 1.1漏洞简介 Oracle weblogic反序列化远程命令执行漏洞,是根据weblogic的xmldecoder反序列化漏洞,只是通过构造巧妙的利用链可以对Oracle官方历年来针对 ...

  7. 学习笔记--Java标识符

    Java标识符 /** * 关于 Java 语言当中的标识符 * * 1. 什么是标识符? * - 在 Java 源程序当中凡是程序员有权利自己命名 * - 标识符可以标识(类名.方法名.变量名.常量 ...

  8. 深入理解Java虚拟机之自己编译JDK

    题外话 最近在阅读<深入理解Java虚拟机>,其中有一小节实战是自己编译JDK,实际操作下来后遇到问题不少,为此特地记录,也希望可以给大家带来一些参考! 前置准备 平台及工具:Window ...

  9. 如何提高docker容器的安全性

    一. 概述 Docker 容器一直是开发人员工具箱的重要组成部分,使开发人员能够以标准化的方式构建.分发和部署他们的应用程序.毫无疑问,这种吸引力的增加伴随着容器化技术的相关安全问题.他们可以很容易地 ...

  10. 龙芯 3A4000 安装 Debian stable

    2022-01-17 版权声明:原创文章,未经博主允许不得转载 3A5000 开始,龙芯转向 loongarch ,新的架构虽然甩掉了历史包袱,但也需要一段时间来积累生态.在这半年多的时间里, loo ...