zoukankan      html  css  js  c++  java
  • 「Luogu P5621」[DBOI2019]德丽莎世界第一可爱

    Description

    (简化题意)

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

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

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

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

    求合法路径权的最大值。

    Hint

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

    Solution

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

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

    状态转移显而易见:

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

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

    我们观察到 状态转移方程中的限制条件 (y_i ge y_j , z_ige z_j , t_ige 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_ige z_j , t_ige t_j)(j) 然后对其 kdt 中的点权求最值就可以完成了。

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

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : Luogu P5621 [DBOI2019]德丽莎世界第一可爱
     */
    #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 了 这个应该可以自己写出来。

  • 相关阅读:
    Redis 2种持久化模式的缺陷
    我看过得最易懂的一段AOP的解释
    mysql-高性能索引策略
    几款效率神器助你走上人生巅峰
    shell脚本报错:"[: =: unary operator expected"
    CentOS7中使用iptables
    php foreach用法和实例
    shell 学习四十五天---xargs
    chain issues incorrect order,EXtra certs,Contains anchor
    Ubuntu 能ping通DNS 地址 无法解析域名
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12610593.html
Copyright © 2011-2022 走看看