题目传送门
在一个四维坐标系中,给定 \(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维护偏序关系,
需要记录子树内最大值以及出现次数,考虑以下几个方面。
#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); }