[Luogu P5621] [DBOI2019]德丽莎世界第一可爱

Description

(简化题意)

给定 \(n\) 个四维点,第 \(i\) 个点为 \((x_i,y_i,z_i,t_i)\) 。

每个点都带有一个点权,第 \(i\) 个点的点权为 \(w_i\)。

对于一个合法的路径,满足经过的点的四个坐标全部单调不降。

路径的权定义为该路径所经过点的权值和。

求合法路径权的最大值。

Hint

\(1\le n\le 5\times 10^4\),答案在 C++ 的 long long 范围内。

Solution

显然可以先按第一维( \(x\) )升序排序,然后动态规划:

设 \(f_i\) 为前 \(i\) 个点构成合法路径所能得到路径权的最大值。

状态转移显而易见:

\[f_i = \max\{ f_j | i < j , y_i \ge y_j , z_i\ge z_j , t_i\ge t_j \} + w_i
\]

但这样直接做的时间复杂度是 \(\Theta(n^2)\) 的,过不了。

我们观察到 状态转移方程中的限制条件 \(y_i \ge y_j , z_i\ge z_j , t_i\ge t_j\) 是不是有点像偏序问题?

那么就可以使用 CDQ 分治 或 K-D Tree 了。这里放一个 kdt 的,cdq 这个坑留给之后的题解。

kdt 结点需要存储的信息:

  • 左右儿子结点的编号,包含这整个子树的矩形的信息,子树大小(重构用),这个结点代表的三维点(排序已经降低了 1 维),其中点带权。 这些都是 kdt 维护的常规信息
  • 整个子树中所有点的点权的最大值。

我们可以把每次更新 \(f_i\) 的值,换做 将第 \(i\) 个点,以 \(f_i\) 为点权插入到 kdt 中。

那么还有就是求 \(f_i\) 的值。我们在 kdt 中找出所有满足 \(y_i \ge y_j , z_i\ge z_j , t_i\ge t_j\) 的 \(j\) 然后对其 kdt 中的点权求最值就可以完成了。

在查询时还有一个剪枝技巧,细节详见代码。

Code

#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;

namespace fastIO_int{
	inline long long get_ll()
	{
		long long x=0;int f=1;char c=getchar();
		while(c<‘0‘||c>‘9‘)
		{
			if(c==‘-‘)f=-1;
			c=getchar();
		}
		while(c>=‘0‘&&c<=‘9‘)
			x=x*10+c-‘0‘,c=getchar();
		return f*x;
	}

	void read(){}
	template<class T1,class ...T2>
	void read(T1 &i,T2&... rest)
	{
		i=get_ll();
		read(rest...);
	}

	void put_ll(long long x)
	{
		if(x<0)putchar(‘-‘),x=-x;
		if(x>9)put_ll(x/10);
		putchar(x%10+‘0‘);
	}

	void write(){}
	template<class T1,class ...T2>
	void write(T1 i,T2... rest)
	{
		put_ll(i),putchar(‘ ‘);
		write(rest...);
	}
};
using fastIO_int::read;
using fastIO_int::write;

const int N=5e4+5;
const int K=3;
struct Point{
	long long dat[K];
	long long val;
};

struct Node{
	int ch[2];
	long long Max[K],Min[K];
	long long maxv;
	int size;
	Point p;
}t[N];
#define lc(u) t[u].ch[0]
#define rc(u) t[u].ch[1]
int root=0;
int total=0;
stack<int> pool;

inline int create()
{
	if(!pool.empty())
	{
		int ret=pool.top();
		return pool.pop(),ret;
	}
	return ++total;
}
inline void maintain(int u)
{
	for(register int i=0;i<K;i++)
	{
		t[u].Max[i]=t[u].Min[i]=t[u].p.dat[i];
		if(lc(u))
		{
			t[u].Max[i]=max(t[u].Max[i],t[lc(u)].Max[i]);
			t[u].Min[i]=min(t[u].Min[i],t[lc(u)].Min[i]);
		}
		if(rc(u))
		{
			t[u].Max[i]=max(t[u].Max[i],t[rc(u)].Max[i]);
			t[u].Min[i]=min(t[u].Min[i],t[rc(u)].Min[i]);
		}
	}
	t[u].maxv=max(t[u].p.val,max(t[lc(u)].maxv,t[rc(u)].maxv));
	t[u].size=t[lc(u)].size+t[rc(u)].size+1;
}

namespace rebuild
{
	const double A=0.75;
	Point pnt[N];
	int dim,len;
	bool cmp(const Point &x,const Point &y){
		return x.dat[dim]<y.dat[dim];
	}
	int build(int l,int r,int d)
	{
		if(l>r) return 0;
		int mid=(l+r)>>1,u=create();
		dim=d,nth_element(pnt+l,pnt+mid,pnt+r+1,cmp);
		t[u].p=pnt[mid];
		lc(u)=build(l,mid-1,(d+1)%3);
		rc(u)=build(mid+1,r,(d+1)%3);
		return maintain(u),u;
	}
	void travel(int u)
	{
		if(!u) return;
		travel(lc(u));
		pnt[++len]=t[u].p;
		pool.push(u);
		travel(rc(u));
	}
	bool balance(int u)
	{
		if(double(t[lc(u)].size)>=A*double(t[u].size)) return 0;
		if(double(t[rc(u)].size)>=A*double(t[u].size)) return 0;
		return 1;
	}
	void work(int &u,int d)
	{
		if(balance(u)) return;
		len=0,travel(u);
		u=build(1,len,d);
	}
}

void insert(Point p,int &u=root,int d=0)
{
	if(!u)
	{
		t[u=create()].p=p;
		return maintain(u);
	}
	if(p.dat[d]<=t[u].p.dat[d]) insert(p,lc(u),(d+1)%3);
	else insert(p,rc(u),(d+1)%3);
	maintain(u),rebuild::work(u,d);
}
bool inside(Point p,int u)
{
	for(register int i=0;i<K;i++)
		if(p.dat[i]<t[u].Max[i]) return 0;
	return 1;
}
bool outside(Point p,int u)
{
	for(register int i=0;i<K;i++)
		if(p.dat[i]<t[u].Min[i]) return 1;
	return 0;
}
bool check_in(Point p,Point q)
{
	for(register int i=0;i<K;i++)
		if(p.dat[i]<q.dat[i]) return 0;
	return 1;
}
void query(Point p,long long &ret,int u=root)
{
	if(!u||outside(p,u)) return;
	if(ret>=t[u].maxv) return;//剪枝:如果找过的答案已经不小于这个子树的点权最大值,那么这个子树也不可能对答案产生贡献,故直接 return。
	if(inside(p,u)) return void(ret=max(ret,t[u].maxv));
	if(check_in(p,t[u].p)) ret=max(ret,t[u].p.val);
	query(p,ret,lc(u)),query(p,ret,rc(u));
}
#undef lc
#undef rc

struct Item{
	long long H,rec;
	Point p;
	void read_data(){
		read(H);
		for(register int i=0;i<K;i++)
			read(p.dat[i]);
		read(rec);
	}
	bool operator <(const Item &t)const{
		if(H!=t.H) return H<t.H;
		for(register int i=0;i<K;i++)
		{
			if(p.dat[i]<t.p.dat[i]) return true;
			if(p.dat[i]>t.p.dat[i]) return false;
		}
		return false;
	}
}itm[N];
int n;
signed main()
{
	read(n);
	for(register int i=1;i<=n;i++)
		itm[i].read_data();
	sort(itm+1,itm+1+n);
	long long ans=0ll;
	for(register int i=1;i<=n;i++)
	{
		long long temp=0;
		query(itm[i].p,temp);
		temp+=itm[i].rec;
		ans=max(ans,temp);
		itm[i].p.val=temp;
		insert(itm[i].p);
	}
	write(ans);
	return 0;
}

如果您还觉得慢的话,这里还有一个优化技巧:

我们不妨将整棵树建好(就这 \(n\) 个点,没跑),初始所有点权为 \(0\) ,插入操作则以 修改 代替。这样既不会导致树不平衡,也不需要任何重构了。

代码 spfa 了 这个应该可以自己写出来。

原文地址:https://www.cnblogs.com/-Wallace-/p/12610593.html

时间: 2024-11-03 12:54:45

[Luogu P5621] [DBOI2019]德丽莎世界第一可爱的相关文章

愚工钻孔机如何做到世界第一:227版本超多改进(2)

接上文<愚工钻孔机如何做到世界第一:不断改进篇 自动镜像(1)>:ref://6040 1.不连清洗机的钻孔机,防止掉落玻璃 如果不链接清洗机,工人忘记在下片区忘记把玻璃抬下来,结果机器一运行,导致玻璃从出片段砸下.227版本后,可加装一个行程开关,如上图所示:红色圆圈部分为行程开关,建议伸出长度可调,这样方便遇到大玻璃可以打孔. 注意点: 1).从电控箱链接3D-到行程开关的1#端子,2#端子连接线到1#PLC(AFPX-38AT)的X17 2).软件进行设置:手动->系统设置->

愚工钻孔机如何做到世界第一:不断改进篇 自动镜像(1)

立式钻孔机在建筑行业,主要加工产品为栏杆.点式幕墙.点式幕墙高利润产品为夹层中空.一个点式幕墙的常见结构如下: *蓝色的为玻璃:深灰色为结构胶:白色为PVB:黄色的为铝条+分子筛:A为最外片 其中BA需要打沉孔,用普通立式钻孔机有难度,必须是伺服进刀的立式钻孔机方有可能性,愚工立钻见:ref://6039 当BA片为Low-E时,会出现一个很有意思的现象:BA片和A片所有的孔位为镜像关系!我们知道,愚工智能钻孔机全部都是采用办公室绘制打孔的玻璃,在车间只要扫描下就可以,遇到这个情况,总不可能要求

Chrome浏览器使用率世界第一

来自研究公司Net Market Share发布的数据显示,从桌面浏览器的流量来看,在4月份,Chrome的市场份额为41.6%,IE的市场份额为41.3%.这是Chrome首次超越IE,成为了世界第一.浏览器一直是各大互联网巨头的必争之地,因为浏览器是一个重要的入口,所以各家对于浏览器都是非常重视.微软在操作系统上是霸主,但是在浏览器中,却被谷歌超越了.不过,这也不奇怪,微软现在把精力都放在了Edge浏览器身上,对于IE,微软基本算是放弃了.Edge浏览器内置于Windows 10版本中,且仅

中国人被“清朝GDP世界第一”忽悠了!

中国人被"清朝GDP世界第一"忽悠了!"鸦片战争前的清朝GDP世界第一",这一说法在中国流传非常广.追根溯源,最早提出这一观点的似乎是英国学者麦迪森,他的一项猜測称,1820年中国GDP占全球总量的33%(英国为5.2%),他的论述被专业人士普遍觉得不严谨.但在中国,非常多非专业人士看重这样的说法对思想的启示性,增加传播行列,终于导致舆论场对它深信不疑. GDP是现代经济概念,可以相对准确地对现代国家经济状况进行描写叙述,反映它们之间的竞争力.用这套体系来做历史研究

张艾迪(创始人):世界冠军.世界第一

Eidyzhang是AOOOiA.Global的创始人.她是世界第一女孩.世界第一互联网女孩.她是整个世界70亿分之1的概率.她是个天才女孩.她是整个世界70亿人的第一名.她是世界第一.世界冠军.她出生在亚洲的中国的一个普通的农户家庭.5岁随着父母迁移至邻近的县城生活. 刚刚来到学校的Eidyzhang就像是功夫熊猫一样似乎有点脏的熊猫宝宝;但是Eidyzhang具有与生俱来的天才资质和对待事物不同的思想.观点.看法.这些让她在小的时候就有些与众不同;她很积极.很喜欢笑.甚至很喜欢在课堂上讲话.

美国为何往往在各个领域都是世界第一?

转:http://c.m.163.com/news/a/BSVPFJBG05159NLE.html?spss=newsapp&spsw=1 美国为什么是世界第一?虽然有各种各样的分析,没有完整的结论,但是大多同意,其中一个重要的原因就是美国的创造力——源源不断的创造力.我国一位负责商贸的官员曾经对美国同仁表示,他常常惊讶于美国无穷尽的创造力:微软.雅虎.谷歌.YUTUBE.TWITTER.世界各国具有前瞻性的人士也许在猜测,下一个微软或者谷歌将会是在哪一个领域?将会给人类带来什么样的变化?将给投

湿地边建“世界第一高楼” 长沙大泽湖湿地生态恶化

经长期观察,当地环保人士发现,受周边远大科技集团世界第一高楼“天空城市”项目建设等因素影响,长沙市大泽湖天然湿地生存状态日益恶化. 据长沙市野生动植物保护协会介绍,大泽湖位于湖南省长沙市望城滨水新区,被誉为“长沙最美湿地”,也是长沙最后一片天然湿地.这里距东洞庭湖国家级自然保护区仅约40公里,不仅是鸟类顺湘江迁徙通道长沙段上最后的“驿站”,而且是不少冬候鸟越冬地和夏候鸟的繁殖地.一些国家重点保护鸟网络pos机类大泽湖均有记录,国家一级www.syzczx.com保护动物白鹤也曾在这里出现过. 2

平安金融壹账通获机器阅读理解顶级赛事(SQuAD)世界第一

平安金融壹账通又有重大突破!继在人类情绪理解竞赛多次登顶之后, 平安金融壹账通GammaLab又在另一项国际顶级赛事上斩获第一.近日,在由斯坦福大学发起的机器阅读理解竞赛(Stanford Question Answering Dataset,SQuAD)中,平安金融壹账通以领先的技术脱颖而出,位列榜单第一位. SQuAD被誉为自然语言处理领域金字塔尖的比赛,旨在促进智能搜索引擎的发展.该赛事吸引了来自谷歌.微软亚研院.阿里达摩院.科大讯飞.IBM以及复旦大学等最顶尖的企业及学校的激烈角逐.平安

OceanBase拿下世界第一!王坚博士又一成就!

OceanBase拿下世界第一!王坚博士又一成就! 中国自研数据库OceanBase拿下世界第一!性能超老牌数据库Oracle 100% 继5G移动通信和高端芯片之后,中国技术在又一个“核高基”领域取得了重大突破. 10月2日,据权威机构国际事务处理性能委员会(TPC,Transaction Processing Performance Council)官网披露,中国蚂蚁金服自主研发的金融级分布式关系数据库OceanBase,在被誉为“数据库领域世界杯”的TPC-C基准测试中,打破了由美国公司O