简单题意

给定 \(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. Eclipse导入Solr源码Version5.5.3

    将Solr的5.5.3版本源码导入Eclipse, Solr源码需要使用ant构建后才能导入Eclipse. 1.JDK安装 Solr要求jdk1.7+: 请参考Windows下安装配置jdk 2.A ...

  2. VNC的安装和使用

    说明 VNC (Virtual Network Console)是虚拟网络控制台的缩写.它是能远程连入Linux进行图形化操作. VNC百度百科介绍:https://baike.baidu.com/i ...

  3. svn钩子脚本

      使用钩子脚本的意义: 我们在测试环境中搭建好svn后,开发人员可以正常的上传和下载写好的代码了.但是上传到测试服务器,在版本库中(tshop)是看不到这些数据的.所以在测试环境中(linux测试服 ...

  4. Python+flask+flask-apscheduer实现定时下发任务

    Python+flask+flask-apscheduer实现定时下发任务 背景: 使用python+flask+mamaca实现的自动化用例管理平台,可以下发任务到具体的节点,进行执行测试用例,没有 ...

  5. 基于Apache Hudi + Flink的亿级数据入湖实践

    本次分享分为5个部分介绍Apache Hudi的应用与实践 实时数据落地需求演进 基于Spark+Hudi的实时数据落地应用实践 基于Flink自定义实时数据落地实践 基于Flink+Hudi的应用实 ...

  6. 用jquery实现省市联动

    <!-- 需求: [1] 动态生成省份选择框. [2] 当选择了省份的某一项时, 动态改变 城市选择中的列表项. --> <!DOCTYPE html> <html la ...

  7. [解决] No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android

    前端时间项目组让我改一个比较老的项目,说是用Android Studio2.3版本可以直接运行,于是我下载了一个2.3.2的,结果出现了一堆问题,总结下: 首先导入项目后build完直接报出:No t ...

  8. 【刷题-LeetCode】147 Insertion Sort List

    Insertion Sort List Sort a linked list using insertion sort. A graphical example of insertion sort. ...

  9. C# 实现NPOI的Excel导出

    技术点: 1.自定义attribute属性 2.通过反射取类及其属性的attribute属性值 3.NPOI包常用属性及方法(我也仅仅知道用到过的,陌生的要么见名知意,要么百度查) 实现功能点: Li ...

  10. jQuery ajax get与post后台交互中的奥秘

    这两天在做关注功能模块(类似于Instagram).多处页面都需要通过一个"关注"按钮进行关注或者取消该好友的操作.一个页面对应的放一个按钮,进行操作.效率低维护性差.因此想通过j ...