Sol.

  题目要求一个数对序列的二维最长下降子序列,我们称其为 Q。并求出每一个元素分别在可能的 Q 中出现了多少次。

  直接 Dp,时间复杂度 \(O(n^2)\) 不行。考虑 CDQ 分治 优化 Dp。即我们每次在分治时,先递归左半部分,在根据左半部分转移右半部分,最后递归右半部分。嗯。很有道理。

  CDQ 维护两组 pair 值,first 统一表示长度,second 统一表示方案数,而 \(dp_{i, 0}\) 和 \(dp_{i, 1}\) 分别指以 \(i\) 结尾和以 \(i\) 开头的 Q 的信息,因为是二维信息再加一位时间,所以大体框架类似三维偏序。

  只不过在 CDQ 中的分治归并排序必须排但又不完全排,即要先拎出来放进备份,然后在备份上操作,这样就可以让当前还未内部转移的右半部分保持顺序不动,然后在后面在递归进行内部转移。而备份上的操作又可以把左半部分到右半部分的外部转移做了。嗯。可以打了。

  补充一下,得出 \(dp_{i, 0}\) 和 \(dp_{i, 1}\) 需要正反跑两次 CDQ,时间复杂度 \(O(n\log^2n)\)。


Code.

#include <cstdio>
#include <algorithm>
using namespace std; int Abs (int x) { return x < 0 ? -x : x; }
int Max (int x, int y) { return x > y ? x : y; }
int Min (int x, int y) { return x < y ? x : y; } int Read () {
int x = 0, k = 1;
char s = getchar();
while (s < '0' || s > '9') {
if(s == '-')
k = -1;
s = getchar ();
}
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
} void Write (int x) {
if(x < 0)
x = -x, putchar ('-');
if(x > 9)
Write (x / 10);
putchar (x % 10 + '0');
} void Print (int x, char s) { Write (x), putchar (s); } const int MAXN = 5e4 + 5; bool Type;
// 把两个 CDQ Rua 到一起又要在一些细节有所区分,所以开个 bool。
int Num[MAXN], Len;
#define pid pair<int, double>
// long long 是假的,这道题中间过程不止 long long。
// 而 double 钟爱存储大小,原因因此舍弃一些精度,而这刚好符合我们的需求。
pid Dp[MAXN][2], BIT[MAXN][2]; pid operator + (pid a, pid b) { // pair 的加法重载。
return make_pair (a.first + b.first, a.second + b.second);
} pid Max_Pair (pid x, pid y) { // 便于方程转移的手写 Max。
if (x.first != y.first)
return x.first > y.first ? x : y;
return make_pair (x.first, x.second + y.second);
} int Low_Bit (int x) { return x & -x; } void Clear (int k) {
if (!Type) {
for (int i = k; i; i -= Low_Bit(i))
BIT[i][0] = make_pair (0, 0);
}
else {
for (int i = k; i <= Len; i += Low_Bit(i))
BIT[i][1] = make_pair (0, 0);
}
} void Update (int k, pid x) {
if (!Type) {
for (int i = k; i; i -= Low_Bit(i))
BIT[i][0] = Max_Pair (BIT[i][0], x);
}
else {
for (int i = k; i <= Len; i += Low_Bit(i))
BIT[i][1] = Max_Pair (BIT[i][1], x);
}
} pid Query (int k) {
pid Res = make_pair (0, 0);
if (!Type) {
for (int i = k; i <= Len; i += Low_Bit(i))
Res = Max_Pair (Res, BIT[i][0]);
return Res;
}
else {
for (int i = k; i; i -= Low_Bit(i))
Res = Max_Pair (Res, BIT[i][1]);
return Res;
}
}
// 将两颗 BIT Rua 在一起,用 Type 使得两个 CDQ 维护的信息刚好反过来。 struct Node {
int h, v, Id;
Node () {}
Node (int H, int V, int I) {
h = H, v = V, Id = I;
}
} q[MAXN], Tmp[MAXN];
// Tmp 即我们操作的备份。 bool Check (int x, int y) { return !Type ? x >= y : x <= y; }
bool Cmp (Node x, Node y) { return !Type ? x.h > y.h : x.h < y.h; }
// 两种不同的排序方式,因偏序实现不同因人而异。用 Type 控制相反。 void CDQ (int l, int r) {
if (l == r)
return ;
int Mid = (l + r) >> 1;
CDQ (l, Mid);
for (int i = l; i <= r; i++)
Tmp[i] = q[i];
sort (Tmp + l, Tmp + Mid + 1, Cmp);
sort (Tmp + Mid + 1, Tmp + r + 1, Cmp);
// 排序,又不完全排序。注意这里是在备份上操作,以完成偏序。
for (int i = l, j = Mid + 1; j <= r; j++) {
while (i <= Mid && Check (Tmp[i].h, Tmp[j].h))
Update (Tmp[i].v, Dp[Tmp[i].Id][Type]), i++ /*, printf ("%d %d\n", Type, i)*/ ; // 遍历新的可能对当前 j 产生转移贡献的 i。
Dp[Tmp[j].Id][Type] = Max_Pair (Dp[Tmp[j].Id][Type], Query (Tmp[j].v) + make_pair (1, 0));
// 转移方程。Type 控反。
// printf ("%d %d %d %d\n", Type, Tmp[j].Id, Dp[Tmp[j].Id][Type].first, (Query (Tmp[j].v) + make_pair (1, 0)).first);
}
for (int i = l; i <= Mid; i++)
Clear (Tmp[i].v);
// 记得将 BIT 清空,不影响以后的转移。
CDQ (Mid + 1, r);
} /*
133 行,曾出现了一个 ub。
Update (Tmp[i].v, Dp[Tmp[i].Id][Type]), i++;
不等价于
Update (Tmp[i].v, Dp[Tmp[i++].Id][Type]);
原因或许是函数传参从右到左。
*/ int main () {
int n = Read ();
for (int i = 1; i <= n; i++) {
q[i].h = Read (), q[i].v = Read (), q[i].Id = i;
Num[i] = q[i].v;
}
sort (Num + 1, Num + n + 1);
Len = unique (Num + 1, Num + n + 1) - Num - 1;
for (int i = 1; i <= n; i++)
q[i].v = lower_bound (Num + 1, Num + Len + 1, q[i].v) - Num;
// BIT 和值域有关,所以离散化。 for (int i = 1; i <= n; i++)
Dp[i][0] = Dp[i][1] = make_pair (1, 1);
// Dp 初值,即当前 Q 只有 i 一个元素。
CDQ (1, n);
reverse (q + 1, q + n + 1);
Type = 1, CDQ (1, n); pid Ans = make_pair (0, 0);
for (int i = 1; i <= n; i++)
Ans = Max_Pair (Ans, Dp[i][0]);
Print (Ans.first, '\n');
// for (int i = 1; i <= n; i++)
// printf ("%d %f %d %f\n", Dp[i][0].first, Dp[i][0].second, Dp[i][1].first, Dp[i][1].second);
for (int i = 1; i <= n; i++)
if (Dp[i][0].first + Dp[i][1].first - 1 == Ans.first)
// 判断当前 i 是否被至少一个 Q 所包含
printf ("%.5f ", Dp[i][0].second * Dp[i][1].second / Ans.second);
else
printf ("0.00000 ");
return 0;
} /*
180 行也曾有一个未发现原因的 ub。
printf ("0.00000 ");
不等价于
printf ("%.5f ", 0);
*/

Solution -「SDOI2011」拦截导弹的更多相关文章

  1. Solution -「构造」专练

    记录全思路过程和正解分析.全思路过程很 navie,不过很下饭不是嘛.会持续更新的(应该). 「CF1521E」Nastia and a Beautiful Matrix Thought. 要把所有数 ...

  2. Solution -「原创」Destiny

    题目背景 题目背景与题目描述无关.签到愉快. 「冷」 他半靠在床沿,一缕感伤在透亮的眼眸间荡漾. 冷见惆怅而四散逃去.经历嘈杂喧嚣,感官早已麻木.冷又见空洞而乘隙而入.从里向外,这不是感官的范畴. 他 ...

  3. Solution -「GLR-R2」教材运送

    \(\mathcal{Description}\)   Link.   给定一棵包含 \(n\) 个点,有点权和边权的树.设当前位置 \(s\)(初始时 \(s=1\)),每次在 \(n\) 个结点内 ...

  4. Solution -「WF2011」「BZOJ #3963」MachineWorks

    \(\mathcal{Description}\)   Link.   给定你初始拥有的钱数 \(C\) 以及 \(N\) 台机器的属性,第 \(i\) 台有属性 \((d_i,p_i,r_i,g_i ...

  5. Solution -「LOCAL」二进制的世界

    \(\mathcal{Description}\)   OurOJ.   给定序列 \(\{a_n\}\) 和一个二元运算 \(\operatorname{op}\in\{\operatorname{ ...

  6. Solution -「SHOI2016」「洛谷 P4336」黑暗前的幻想乡

    \(\mathcal{Description}\)   link.   有一个 \(n\) 个结点的无向图,给定 \(n-1\) 组边集,求从每组边集选出恰一条边最终构成树的方案树.对 \(10^9+ ...

  7. Solution -「LOCAL」大括号树

    \(\mathcal{Description}\)   OurTeam & OurOJ.   给定一棵 \(n\) 个顶点的树,每个顶点标有字符 ( 或 ).将从 \(u\) 到 \(v\) ...

  8. Solution -「ZJOI2012」「洛谷 P2597」灾难

    \(\mathcal{Description}\)   link.   给定一个捕食网络,对于每个物种,求其灭绝后有多少消费者失去所有食物来源.(一些名词与生物学的定义相同 w.)   原图结点数 \ ...

  9. Solution -「JSOI2008」「洛谷 P4208」最小生成树计数

    \(\mathcal{Description}\)   link.   给定带权简单无向图,求其最小生成树个数.   顶点数 \(n\le10^2\),边数 \(m\le10^3\),相同边权的边数不 ...

随机推荐

  1. 读 Angular 代码风格指南

    读 Angular 代码风格指南 本文写于 2021 年 1 月 17 日 原文地址:Angular 文档 该文章拥有完整的代码风格指南--大到如何编排文件夹,小到如何进行变量命名都涉及.但是与 ng ...

  2. 152. Maximum Product Subarray - LeetCode

    Question 152. Maximum Product Subarray Solution 题目大意:求数列中连续子序列的最大连乘积 思路:动态规划实现,现在动态规划理解的还不透,照着公式往上套的 ...

  3. python之部分内置函数与迭代器与异常处理

    目录 常见内置函数(部分) 可迭代对象 迭代器对象 for循环内部原理 异常处理 异常信息的组成部分 异常的分类 异常处理实操 异常处理的其他操作 for循环本质 迭代取值与索引取值的区别 常见内置函 ...

  4. Docker 与 K8S学习笔记(二十三)—— Kubernetes集群搭建

    小伙伴们,好久不见,这几个月实在太忙,所以一直没有更新,今天刚好有空,咱们继续k8s的学习,由于我们后面需要深入学习Pod的调度,所以我们原先使用MiniKube搭建的实验环境就不能满足我们的需求了, ...

  5. 《Unix 网络编程》08:基本UDP套接字编程

    基本UDP套接字编程 系列文章导航:<Unix 网络编程>笔记 UDP 概述 流程图 recvfrom 和 sendto #include <sys/socket.h> ssi ...

  6. 【clickhouse专栏】数据库、数据仓库之间的区别与联系

    从本篇文章开始,笔者打算写一个系列的<clickhouse专栏>,其全称是Click Stream,Data WareHouse,简称ClickHouse.从其全称中的"Data ...

  7. 《Unix 网络编程》14:高级 I/O 函数

    高级 I/O 函数 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ...

  8. LoRa和NB-IoT会长期共存吗?

    物联网有很多无线通信技术,主要是Zigbee,WiFi,蓝牙和Z-wave等短距离通信技术,另一种是LPWAN.LPWAN的两种类型之一在未授权的频谱上工作.另一种是2G/3G/4G蜂窝通信技术,该技 ...

  9. Java注释、标识符、关键字

    Java注释.标识符.关键字 注释 单行注释以"//" 开始:多行注释以 "/*" 开始,以"*/"结束:文档注释以 "/**&q ...

  10. Puppeteer学习笔记 (2)- Puppeteer的安装

    本文链接:https://www.cnblogs.com/hchengmx/p/11009849.html 1. node的下载安装 由于puppeteer是nodejs的一个库,所以首先需要安装no ...