题目传送门


题目大意

在一个四维坐标系中,给定 \(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. CSS之浮动Float

    前言 提到浮动,前端的小伙伴肯定都不陌生,但是随着弹性布局等等一些更好用的标准出来后,用在布局方面少了很多,当初我刚开始接触前端的时候,很习惯用浮动来给元素改变定位,当时还并不是很流行flexbox布 ...

  2. 学习go语言编程之并发编程

    并发基础 并发包含如下几种主流的实现模型: 多进程 多线程 基于回到的非阻塞/异步IO 协程 协程 与传统的系统级线程和进程相比,协程最大的优势在于"轻量级",可以轻松创建上百万个 ...

  3. Kotlin 协程二 —— 通道 Channel

    目录 一. Channel 基本使用 1.1 Channel 的概念 1.2 Channel 的简单使用 1.3 Channel 的迭代 1.4 close 关闭 Channel 1.5 Channe ...

  4. .NET Core 集成微信支付签名错误

    .NET Core 集成微信支付签名错误 The provided data is tagged with 'Universal' class value '16', but it should ha ...

  5. Kconnect使用sftp windows自定义协议

    终于有时间写点东西了,上次写东西已经是三个月之前了.自从出现了觉得一个月写一篇文章也没关系的想法之后就已经完全忘记有这回事儿了.一直觉得没有足够的时间,但是又想写出质量比较好的文章,所以就一直没有动笔 ...

  6. C++ 模板的笔记2

    C++模板的笔记2 关于可变参函数模板借鉴了一部分笔记,感谢大佬 类模板中的嵌套 类模板可以嵌套其他类模板,就像普通类可以嵌套其他普通类一样.嵌套的类模板可以访问外部类模板的成员,包括私有成员. 示例 ...

  7. C++ //类模板与友元 //全局函数类内实现 -直接在类内声名由于即可 //全局函数类外实现 -需要提前让编译器知道全局函数的存在

    1 //类模板与友元 2 //全局函数类内实现 -直接在类内声名由于即可 3 //全局函数类外实现 -需要提前让编译器知道全局函数的存在 4 5 #include <iostream> 6 ...

  8. iOS使用Unity容器动态加载3D模型

    项目背景 我们的APP是一个数字藏品平台,里面的很多藏品需要展示3D模型,3D模型里面可能会包含场景,动画,交互.而对应3D场景来说,考虑到要同时支持iOS端,安卓端,Unity是个天然的优秀方案. ...

  9. electron fiddle 下载 镜像 下载不下来 已解决 electron-api-demos 安装

    fiddle 官网 https://www.electronjs.org/fiddle 一共3步 1. npm config set registry https://registry.npm.tao ...

  10. 提升地理空间分析效率,火山引擎ByteHouse上线GIS能力

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在数字化时代,地理空间分析(Geospatial Analytics)成为辅助企业市场策略洞察的重要手段.无论是广 ...