题目传送门


题目大意

在一个四维坐标系中,给定 \(n\) 个点,问有多少种选择点的方案,

使得这些点排序后任意坐标单调不降,并且选择的点权和最大,同时输出最大值


分析

设 \(f[i]\) 表示最后一个点为\(i\)时的最大点权和,

则 \(f[i]=\max\{f[j]\}+a[i],p[j]\leq p[i]\), \(p\) 为四维坐标

设 \(dp[i]\) 表示在取到最大点权和时的方案数,

则 \(dp[i]=\begin{cases}\sum dp[j'],f[j']=\max\{f[j]\}\\ 1,otherwise\end{cases}\)

这是 \(O(n^2)\) 的做法,考虑用K-D Tree维护偏序关系,

需要记录子树内最大值以及出现次数,考虑以下几个方面。

  • 建树:第一维可以排序后省掉,其实就剩下三维,然后重构这里用替罪羊树的方法
  • 剪枝1:维护子树内每个坐标的最小值和最大值,如果子树内存在一个最小值大于当前坐标,那么无须遍历该子树
  • 剪枝2:如果当前答案比子树内的答案大那么这棵子树不用遍历
  • 剪枝3:如果子树内所有最大值都不小于当前坐标,那么这棵子树的答案可以直接用

代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=80011; const double alp=0.75;
typedef long long lll; lll Ans,ans[N]; int AC,root,n,ran;
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
struct rec{
int X,p[3]; lll w,c;
inline bool operator <(const rec &t)const{
return p[ran]<t.p[ran];
}
}a[N];
inline lll min(lll a,lll b){return a<b?a:b;}
inline lll max(lll a,lll b){return a>b?a:b;}
inline signed mo(int x,int y){return (x+y)%998244353;}
struct KD_Tree{
int mn[N][3],mx[N][3],son[N][2],siz[N],stac[N],TOP,tot;
lll w[N],wc[N]; rec pt[N],p[N];
inline void pup(int now){//上传pushup
for (rr int i=0;i<3;++i){
mn[now][i]=mx[now][i]=p[now].p[i];
if (son[now][0]){
mn[now][i]=min(mn[now][i],mn[son[now][0]][i]);
mx[now][i]=max(mx[now][i],mx[son[now][0]][i]);
}
if (son[now][1]){
mn[now][i]=min(mn[now][i],mn[son[now][1]][i]);
mx[now][i]=max(mx[now][i],mx[son[now][1]][i]);
}
}
w[now]=max(p[now].w,max(w[son[now][0]],w[son[now][1]])),wc[now]=0;//最大值只有三种情况
if (w[now]==p[now].w) wc[now]=p[now].c;
if (w[now]==w[son[now][0]]) wc[now]+=wc[son[now][0]];
if (w[now]==w[son[now][1]]) wc[now]+=wc[son[now][1]];
siz[now]=siz[son[now][0]]+siz[son[now][1]]+1;
}
inline bool balance(int now){return alp*siz[now]>=(max(siz[son[now][0]],siz[son[now][1]]));}//替罪羊树判定平衡
inline void recycle(int now){//回收
if (son[now][0]) recycle(son[now][0]);
stac[++TOP]=now,pt[TOP]=p[now];
if (son[now][1]) recycle(son[now][1]);
}
inline signed build(int l,int r,int Ran){//建树
if (l>r) return 0;
rr int mid=(l+r)>>1,now=stac[mid];
ran=Ran,nth_element(pt+l,pt+mid,pt+1+r),p[now]=pt[mid];
son[now][0]=build(l,mid-1,(Ran+1)%3);
son[now][1]=build(mid+1,r,(Ran+1)%3);
pup(now);
return now;
}
inline void rebuild(int &now,int Ran){//重构
TOP=0,recycle(now);
now=build(1,TOP,Ran);
}
inline void Insert(int &now,rec W,int Ran){//插入
if (!now) now=++tot,p[now]=W;
else{
if (W.p[Ran]<=p[now].p[Ran]) Insert(son[now][0],W,(Ran+1)%3);
else Insert(son[now][1],W,(Ran+1)%3);
}
pup(now);
if (!balance(now)) rebuild(now,Ran);
}
inline bool check0(int j,int i){
for (rr int o=0;o<3;++o)
if (p[j].p[o]>p[i].p[o]) return 0;
return 1;
}
inline bool check1(int j,int i){
for (rr int o=0;o<3;++o)
if (mn[j][o]>p[i].p[o]) return 1;
return 0;
}
inline bool check2(int j,int i){
for (rr int o=0;o<3;++o)
if (mx[j][o]>p[i].p[o]) return 0;
return 1;
}
inline signed query(int now,lll &ans,int x){
rr int f=0;
if (check0(now,x)){
if (ans<p[now].w) f=p[now].c,ans=p[now].w;
else if (ans==p[now].w) f=mo(f,p[now].c);
}
while (son[now][0]){
rr int t=son[now][0];
if (check1(t,x)||ans>w[t]) break;//剪枝1、2,下同
if (check2(t,x)){//剪枝3
if (ans<w[t]) f=wc[t],ans=w[t];
else if (ans==w[t]) f=mo(f,wc[t]);
}else{
rr lll o=ans,NOW=query(t,ans,x);
if (o<ans) o=ans,f=NOW;
else if (o==ans) f=mo(f,NOW);
}
break;
}
while (son[now][1]){
rr int t=son[now][1];
if (check1(t,x)||ans>w[t]) break;
if (check2(t,x)){
if (ans<w[t]) f=wc[t],ans=w[t];
else if (ans==w[t]) f=mo(f,wc[t]);
}else{
rr lll o=ans,NOW=query(t,ans,x);
if (o<ans) o=ans,f=NOW;
else if (o==ans) f=mo(f,NOW);
}
break;
}
return f;
}
}Tre;
bool cmp(rec x,rec y){
if (x.X!=y.X) return x.X<y.X;//在K-D Tree中省略第一维
for (rr int i=0;i<3;++i)
if (x.p[i]!=y.p[i]) return x.p[i]<y.p[i];
return 0;
}
signed main(){
n=iut(),iut();
for (rr int i=1;i<=n;++i) a[i]=(rec){iut(),iut(),iut(),iut(),iut(),0};
sort(a+1,a+1+n,cmp);
for (rr int i=1;i<=n;++i){
Tre.p[i]=a[i],a[i].c=Tre.query(root,ans[i],i);
if (!a[i].c) a[i].c=1;//如果之前没有答案那么方案数为1
a[i].w+=ans[i],Tre.Insert(root,a[i],0);
}
for (rr int i=1;i<=n;++i)
if (Ans<a[i].w) Ans=a[i].w,AC=a[i].c;
else if (Ans==a[i].w) AC=mo(AC,a[i].c);
return !printf("%lld\n%d",Ans,AC);
}

#KD-Tree#洛谷 4849 寻找宝藏的更多相关文章

  1. 洛谷P3959 [NOIP2017]宝藏

    [题目描述] 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋,也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但 ...

  2. 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)

    洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...

  3. 洛谷P2296 寻找道路 [拓扑排序,最短路]

    题目传送门 寻找道路 题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点 ...

  4. AC日记——【模板】Link Cut Tree 洛谷 P3690

    [模板]Link Cut Tree 思路: LCT模板: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 30 ...

  5. 洛谷P2296 寻找道路==codevs3731 寻找道路

    P2296 寻找道路 题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点 ...

  6. 洛谷——P2296 寻找道路

    P2296 寻找道路 题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点 ...

  7. POJ1471 Tree/洛谷P4178 Tree

    Tree P4178 Tree 点分治板子. 点分治就是直接找树的重心进行暴力计算,每次树的深度不会超过子树深度的\(\frac{1}{2}\),计算完就消除影响,找下一个重心. 所以伪代码: voi ...

  8. [NOIP2014] 提高组 洛谷P2296 寻找道路

    题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条 ...

  9. NOIP2014 day2 T2 洛谷P2296 寻找道路

    题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条 ...

  10. 洛谷 [P2296] 寻找道路

    反向BFS预处理,求出所有符合题意的点,再正向BFS,(注意对于边权恒为一的点,BFS,比SPFA高效) 输入时n与m分清 #include <iostream> #include < ...

随机推荐

  1. 【Android 逆向】【ARM汇编】 事前更新和事后更新

    1. 事前更新,事后更新,不更新 不更新 ldr R4, [R1, R2, lsl #1] 相当于 R4 = *(R1 + R2 << 2^1) 之后 R1.R2的值时没有变化的 事前更新 ...

  2. Docker进阶之02-Swarm集群入门实践

    Docker集群概述 Docker集群有2种方案: 1.在Docker Engine 1.12之前的集群模式被称为经典集群,这是通过API代理系统实现的集群,目前已经不再维护. 2.自Docker E ...

  3. itext 生成 PDF

    itext 生成 PDF(一) 转自:https://blog.csdn.net/lcczpp/article/details/125424395   itext生成PDF excel 示例  转自: ...

  4. OsgEarth开发笔记(二):Osg3.6.3+OsgEarth3.1+vs2019x64开发环境搭建(中)

    上一篇:<OsgEarth开发笔记(一):Osg3.6.3+OsgEarth3.1+vs2019x64开发环境搭建(上)>下一篇:敬请期待-   前言  上一篇编译了osg和osgCurl ...

  5. pyqt5中通过pycharm配置designer(win和mac都适用,修改下designer目录路径即可)

    安装 pip install PyQt5 -i https://pypi.douban.com/simple pip install PyQt5-tools -i https://pypi.douba ...

  6. Kotlin 协程五 —— 在Android 中使用 Kotlin 协程

    目录 一.Android MVVM 结构 二.添加依赖 三.在后台线程中执行 3.1 协程解决了什么问题 3.2 保证主线程安全 3.3 withContext 的性能 四.结构化并发 4.1 追踪协 ...

  7. 【Azure 应用服务】PHP项目部署到App Service for Linux环境中,如何修改上传文件大小的限制呢?

    问题描述 PHP项目部署到App Service for Linux环境中,如何修改上传文件大小的限制呢? 问题解答 经过查询Azure App Service官方文档,可能通过在项目根目录下添加.h ...

  8. 【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?

    问题描述 为App Service For Linux配置CI/CD,源代码在GitHub私有库中,在发布时候报错 Cannot find SourceControlToken with name B ...

  9. http-server -S 开启 https 服务

    下载 openssl Win64 OpenSSL v1.1.1k Light http://slproweb.com/download/Win64OpenSSL_Light-1_1_1k.exe 一路 ...

  10. 【leetcode 2949 统计美丽子字符串】

    import java.util.HashMap; import java.util.Map; class Solution { public static void main(String[] ar ...