简单题意

给定 \(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. centos6.5-DNS搭建

    在RHEL6.5中,系统光盘自带了BIND服务的安装文件 安装步骤 准备工作: Service iptables stop    #关闭防火墙    Setenforce 0    关闭selinux ...

  2. nginxWebUI

    nginx网页配置工具 github: https://github.com/cym1102/nginxWebUI 功能说明 本项目可以使用WebUI配置nginx的各项功能, 包括http协议转发, ...

  3. java 访问 太平洋网ip接口,解决前端js 跨域访问失败问题

    前端 js访问太平洋网IP接口地址,返回结果是403 服务器拒绝处理异常, 于是,想到了使用 服务器端访问,然后再将查询结果返回的前端 这是Java的测试源码,[具体的contronller端源码懒得 ...

  4. centos 操作系统优化

    命令提示符优化 修改PS1环境变化 vim /etc/profile #在最后一行添加 export PS1='[\u@\H \w]$' \u ---显示当前登录用户名称 \h ---显示系统主机名称 ...

  5. 第10组 Alpha冲刺 (2/6)

    1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/ ·作业博客:https://edu.cnblogs.com/campus/fzu/FZ ...

  6. Layui table 学习笔记

    templet:'<div>{{createrFormat(d.accounts.name)}}</div>' function createrFormat(o){ retur ...

  7. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  8. 谱分解(SD)

    前提:矩阵A必须可相似对角化! 充分条件: $A$ 是实对称矩阵 $A$ 有 $n$ 个互异特征值 $A^{\wedge} 2=A $ $\mathrm{A}^{\wedge} 2=\mathrm{E ...

  9. Typora中本地图片无法上传CSDN解决方案

    解决方法 本地图片无法上传,我们可以选择使用在线免费图床 把想要使用的图先上传到图床后复制对应的MarkDown语句到typora即可 在这里,给大家推荐一个图床 [图床链接](Image Uploa ...

  10. dart系列之:和null说再见,null使用最佳实践

    目录 简介 不需要初始化对象为null null的三元操作符 如果在使用中需要判断类型是否为空,则不要使用late 本地变量的类型提升 总结 简介 null可能是大家在编写程序中最为头疼的一个东西,稍 ...