简单题意

给定 \(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. Solr集群安装Version5.5.2(cloud模式)

    Solr安装cloud模式,基于Solr的安装版本为5.5.2. 安装规划 Solr IP/机器名 安装软件 运行进程 zdh-7 solr jar zdh-9 solr jar zookeeper ...

  2. PL/SQL连接时,报无法解析指定的字符串

    前言: 工作原因,需要安装PL/SQL连接数据,oracle和PL/SQL都装好了,环境变量也配好了,启动PL/SQL进行连接数据库,结果报"无法解析指定的字符串",连接失败了. ...

  3. unittest_expectedFailure预期用例失败(5)

    在断言用例执行结果时,会出现预期结果与实际结果不一致的情况,此时我们明确知道用例执行结果为FAIL,不想看到打印错误信息怎么办? 使用装饰器@unittest.expectedFailure标记该用例 ...

  4. vue3.0+vite+ts项目搭建--vite.config.ts配置(三)

    vite.config.ts配置 配置路径处理模块 安装ts的类型声明文件 yarn add @types/node -D 通过配置alias来定义路径的别名 resolve: { alias: { ...

  5. JAVA多线程之并发编程三大核心问题

    概述 并发编程是Java语言的重要特性之一,它能使复杂的代码变得更简单,从而极大的简化复杂系统的开发.并发编程可以充分发挥多处理器系统的强大计算能力,随着处理器数量的持续增长,如何高效的并发变得越来越 ...

  6. 如何将Excl内数据导入数据库?

    最近有个Excl表格内的数据需要导入SQL Server数据库内,使用SQL Server Management Studio客户端图形界面操作了一番,步骤还挺多,感觉有必要分享给大家一下,顺便自己也 ...

  7. 《剑指offer》面试题45. 把数组排成最小的数

    问题描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.   示例 1: 输入: [10,2] 输出: "102" 示例 2: 输入: ...

  8. golang gin框架中使用protocol buffers和JSON两种协议

    首先,我使用protobuf作为IDL,然后提供HTTP POST + JSON BODY的方式来发送请求. 能不能使用HTTTP POST + PB序列化后的二进制BODY呢? 做了一下尝试,非常简 ...

  9. promise抛异常,执行队列

    //promise抛出异常 new Promise((resolve,reject)=>{ resolve("成功") }).then(res=>{ if(res != ...

  10. Centos配置yum本地源最简单的办法

    有关centos配置yum本地源的方法 一.前提 先连接镜像 然后在命令行输入如下命令 mount /dev/sr0 /mnt cd /etc/yum.repos.d/ ls 之后会看到如下的界面 二 ...