zoukankan      html  css  js  c++  java
  • #KD-Tree#洛谷 4849 寻找宝藏

    题目传送门


    题目大意

    在一个四维坐标系中,给定 (n) 个点,问有多少种选择点的方案,

    使得这些点排序后任意坐标单调不降,并且选择的点权和最大,同时输出最大值


    分析

    (f[i]) 表示最后一个点为(i)时的最大点权和,

    (f[i]=max{f[j]}+a[i],p[j]leq p[i])(p) 为四维坐标

    (dp[i]) 表示在取到最大点权和时的方案数,

    (dp[i]=egin{cases}sum dp[j'],f[j']=max{f[j]}\ 1,otherwiseend{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
    %d",Ans,AC);
    }
    
  • 相关阅读:
    方法的封装与调用(十)
    适配器设计模式及GenericServlet(九)
    错误页设置,设置HTTP状态码404,500(八)
    设置默认首页(七)
    ServletContext接口(六)
    javax.servlet.ServletConfig接口(五)
    C语言第2天基本运算
    再议extern和include的作用
    C语言中的++和--
    C语言培训第一天
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/14728987.html
Copyright © 2011-2022 走看看