题目传送门


题目大意

在一个四维坐标系中,给定 \(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. 《Similarity-based Memory Enhanced Joint Entity and Relation Extraction》论文阅读笔记

    代码 原文 摘要 文档级联合实体和关系抽取是一项难度很大的信息抽取任务,它要求用一个神经网络同时完成四个子任务,分别是:提及检测.共指消解.实体分类和关系抽取.目前的方法大多采用顺序的多任务学习方式, ...

  2. 第140篇:微信小程序的登录流程

    好家伙,补补补   顶不住了,跑不掉了,这部分的知识还是要补上   来看看微信小程序登录的完整流程   最左边的一列就是前端负责的部分了 几个关键的参数: code:一个用户登录凭证,就是一个临时的t ...

  3. Linux查看文件大小、磁盘使用情况

    1.显示磁盘的可用情况: df -h 2.显示文件夹大小 du -ka folder | sort -rnk 1 | head -n 10

  4. 【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题

    问题描述 Azure Cache for Redis 服务在传输和存储数据时是如何加密呢? 问题回答 一:关于Azure cache for Redis服务在数据传输过程中是如何加密的? 为了确保在A ...

  5. Nebula Graph 源码解读系列 | Vol.06 MATCH 中变长 Pattern 的实现

    目录 问题分析 定长 Pattern 变长 Pattern 与变长 Pattern 的组合 执行计划 拓展一步 拓展多步 保存路径 变长拼接 总结 MATCH 作为 openCypher 语言的核心, ...

  6. centos下配置修改hosts文件以及生效命令详解

    linux服务器hosts文件配置 hosts文件是Linux系统中一个负责IP地址与域名快速解析的文件,以ASCII格式保存在"/etc"目录下,文件名为"hosts& ...

  7. git的 .gitignore 配置概述

    git的 .gitignore 配置概述 学习背景:自己在使用git时发现有时会上传很多无用的配置文件,或者在项目中已经包含一个本地的git仓库,导致上一级项目上传总是报错,所以学习采用gitigno ...

  8. Spring Cloud 系列之Hystrix、Ribbon、Feign 源码剖析(一)引子

    系列目录 Spring Cloud 系列之Hystrix.Ribbon.Feign 源码剖析(一)引子 Spring Cloud 系列之Hystrix.Ribbon.Feign 源码剖析(二)原理概括 ...

  9. 第12章_MySQL数据类型精讲

    第12章_MySQL数据类型精讲 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. MySQL中的数据类型 类型 类型举例 整数类型 TINYINT. ...

  10. Java处理子父级菜单的方式二

    处理存在子父级关系的数据是写代码的过程中常见的操作,前面讲解过使用递归的方法来做, 可以参考这篇博客 https://www.cnblogs.com/yilangcode/p/16831867.htm ...