题目传送门


题目大意

在一个四维坐标系中,给定 \(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. DVWA XSS

    XSS Store hign level <?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_P ...

  2. 学习go语言编程之标准库

    标准库包分类 Golang标准库可以大致按其中库的功能进行以下分类: 分类 对应包 描述 输入输出 bufio,fmt,io,log,flag 这个分类包括二进制以及文本格式在屏幕.键盘.文件以及其他 ...

  3. django1.11和django2.2中namespace的用法

    django1.11中namespace用法 urlpatterns = [ url(r'^user/', include('user.urls', namespace='user')) ] djan ...

  4. .net core6 Autofac依赖注入

    一.引言 .net core6在文件方面是精简了,所以配置方面也发生了部分变化:下面记录下.net core6中怎么配置Autofac 进行依赖注入. 二.项目创建 1).首先引用两个包:在Nuget ...

  5. 【Azure 应用服务】如何让App Service 支持 Delete 方法 

    问题描述 如何让webapp 支持 delete 方法? 在不修改设置的情况下,调用DELETE方法出现405错误 - 方法不被允许 问题解决 基于当前App Service在Windows的环境中运 ...

  6. 浅入Kubernetes(9):了解组件

    本篇主要介绍 Kubernetes 中的架构组成,在前面我们已经学习到了 kubeadm.kubectl,这两个命令行工具是 k8s 组成之一.而前面在搭建集群时,也学到了 master.worker ...

  7. Jepsen 测试框架在图数据库 Nebula Graph 中的实践

    在本篇文章中主要介绍图数据库 Nebula Graph 在 Jepsen 这块的实践. Jepsen 简介 Jepsen 是一款用于系统测试的开源软件库,致力于提高分布式数据库.队列.共识系统等的安全 ...

  8. C++ //类模板分文件编写问题及解决 //第一中解决方式 直接包含源文件 //第二种解决方法 将.h 和 cpp的内容写到一起,将后缀改为.hpp文件

    1 //第一种方式被注释 2 //未被注释是第二种方式 3 //类模板分文件编写问题及解决 4 5 6 #include <iostream> 7 #include <string& ...

  9. SQL之 逻辑库,数据表

    SQL语言三大类 创建逻辑库 创建数据表 例子 数据表其他操作 ps:desc仅仅查看表的结构,不能查看内容 添加字段 ps: 修改字段类型和约束 修改字段名称 删除字段

  10.  liunx上安装django ,启动uwsgi ,语音播报python实现过程

    由于需要做一个语音播报实现,用到的技术是python  ,需要事先搭环境,安装uwsgi  djagno环境,以下内容为百度上找到的好一点的内容,确实照着做成功了,转载一下,下次更好找资料 liunx ...