套娃
题目描述
C u b e r Q Q Cuber\ QQ Cuber QQ 想为 Q u b e r C C Quber\ CC Quber CC 的生日准备一件礼物。他买了 n n n 个套娃,第 i i i 个套娃的长度、宽度、高度分别是 a i , b i , c i a_i,b_i,c_i ai,bi,ci,重量是 w i w_i wi 。当且仅当 a i < a j a_i<a_j ai<aj 且 b i < b j b_i<b_j bi<bj 且 c i < c j c_i<c_j ci<cj 同时满足的时候, 第 i i i 个套娃可以被装进第 j j j 个套娃里。每一个套娃中最多装一个别的套娃。
C u b e r Q Q Cuber\ QQ Cuber QQ 想要给 Q u b e r C C Quber\ CC Quber CC 送一个他所能组装成的最重的套娃(内部可能套了许多层),请你告诉他最重的套娃有多重。
输入格式
第一行输入一个整数 n n n,代表套娃的数量。
接下来的 n n n 行,每行包含四个整数 a i , b i , c i , w i a_i,b_i,c_i,w_i ai,bi,ci,wi ,表示第 i i i 个套娃的长度、宽度、高度。
1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1≤n≤105
1 ≤ a i , b i , c i ≤ 1 0 9 , 0 ≤ w i ≤ 1 0 9 1\le a_i,b_i,c_i\le 10^9,0\le w_i\le 10^9 1≤ai,bi,ci≤109,0≤wi≤109
输出格式
输出共一行,包含一个整数,表示组装成的最重的套娃有多重。
样例 #1
样例输入 #1
5
1 2 3 1
2 2 2 2
3 4 5 3
2 3 4 2
3 3 3 3
样例输出 #1
6
题目链接
思路
- 结构体结点:x,y,z记录套娃大小,w记录套娃自身重量,ans记录该套娃内部能装套娃的最大重量。
- CDQ分治:根据套娃的特性,总是先用次小的套最小的,然后次次小的套次小的,以此类推保证重量最大。所以分治过程中需先对较小部分即左区间分治,然后处理右区间的套娃套左区间的套娃,最后对较大部分即右区间分治。
- 细节处理:1.单个套娃重量的范围是0~1e9,多个套娃的重量相加会爆int,有关重量计算的所有变量和数组记得开longlong。2.用树状数组存储小于等于z值(某个高度)的最大重量,下标是无法承受z即高度的数据范围的。所以要对z即高度进行离散化,构建1到num_z对所有不同有序z值的映射关系。3.由于在分治右区间之前已经进行cmp_y排序打乱了x的顺序,所以在分治右区间之前还需先对区间进行cmp_x排序。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 6;
int n, num_z;
struct node
{
int x, y, z; // 套娃大小
ll w; // 该套娃重量
ll ans; // 里面能套多少重量
};
node a[maxn]; // 读入的元素数组
ll tr[maxn]; // 树状数组
int lowbit(int x) // 获取低位2次幂数
{
return x & -x;
}
void update(int x, ll k) // 单点修改
{
while (x <= num_z)
{
tr[x] = max(tr[x], k);
x += lowbit(x);
}
}
void clean(int x) // 清空树状数组
{
while (x <= kk)
{
tr[x] = 0;
x += lowbit(x);
}
}
ll query(int x) // 前缀和查询
{
ll res = 0;
while (x)
{
res = max(res, tr[x]);
x -= lowbit(x);
}
return res;
}
bool cmp_x(node a, node b)
{
if (a.x == b.x)
if (a.y == b.y)
return a.z < b.z;
else
return a.y < b.y;
else
return a.x < b.x;
}
bool cmp_y(node a, node b)
{
if (a.y == b.y)
if (a.z == b.z)
return a.x < b.x;
else
return a.z < b.z;
else
return a.y < b.y;
}
void cdq(int l, int r)
{
// 保证第一维严格小于
if (a[l].x == a[r].x)
return;
int mid = (l + r) >> 1;
while (mid > l && a[mid].x == a[mid + 1].x)
mid--;
// 优先分治左区间,把小的里面能套的先套了
cdq(l, mid);
// 看大的能不能套小的
// 按第二维排序
sort(a + l, a + mid + 1, cmp_y);
sort(a + mid + 1, a + r + 1, cmp_y);
// 双指针,j指向前一半,i指向后一半
int j = l;
for (int i = mid + 1; i <= r; i++)
{
while (a[j].y < a[i].y && j <= mid)
{
update(a[j].z, a[j].ans + a[j].w);
j++;
}
a[i].ans = max(a[i].ans, query(a[i].z - 1));
}
// 清空树状数组
for (int i = l; i < j; i++)
{
clean(a[i].z);
}
// 最后分治右区间,看大的能不能套大的
sort(a + mid + 1, a + r + 1, cmp_x); // 由于前面cmp_y已经打乱了x,这边右区间分治要再次cmp_x
cdq(mid + 1, r);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
set<int> s; // 去重
unordered_map<int, int> mp; // 离散化
for (int i = 1; i <= n; i++)
{
cin >> a[i].x >> a[i].y >> a[i].z >> a[i].w;
s.insert(a[i].z);
a[i].ans = 0;
}
// 树状数组的上界num_z
num_z = s.size();
// 离散化
int idx = 1;
for (auto it = s.begin(); it != s.end(); it++, idx++)
{
mp[*it] = idx;
}
for (int i = 1; i <= n; i++)
{
a[i].z = mp[a[i].z];
}
// 按第一维排序
sort(a + 1, a + 1 + n, cmp_x);
cdq(1, n); // 分治喵
// 找到最重的一个套娃
ll max_weight = 0;
for (int i = 1; i <= n; i++)
{
max_weight = max(max_weight, a[i].ans + a[i].w);
}
cout << max_weight << '\n';
return 0;
}


1939

被折叠的 条评论
为什么被折叠?



