题解 P4849 【寻找宝藏】

绝顶我为峰

2021-01-07 22:40:07

Solution

## Problem 四维空间,每次可以在每个维度向正半轴移动一个单位,可以获得途经的所有点的贡献。求从 $(1,1,1,1)$ 到 $(m,m,m,m)$ 的所有路径中的最大贡献,并求出方案数。 ## Solution 首先有一个非常 naive 的 dp 做法,对于每个可以转移的点对连一条有向边,最后在 DAG 上 dp。 这种做法是 $O(n^2)$ 的,显然无法通过。 考虑优化这个 dp,我们注意到一个点对另一个点可以产生贡献,当且仅当二者满足偏序关系。 这是一个裸的四维偏序,用 CDQ 优化 dp 即可。 ~~众所周知~~高维偏序每套一层 CDQ 就可以用一个 log 的复杂度降掉一维,于是我们有了一个 CDQ 套 CDQ 的做法,复杂度 $O(nlog^3n)$,可以通过本题。 默认大家已经会了 CDQ。三维偏序大家都会,这里讲一下怎样实现四维偏序(这个做法也适用于更高维的偏序)。 我们考虑一个序列,第一维有序,那么对于一个区间 $[l,r]$,考虑计算 $[l,mid]$ 对 $[mid+1,r]$ 的贡献。 由于第一维有序,$[l,mid]$ 第一维一定小于 $[mid+1,r]$ 第一维,那么我们直接给两段序列打上标记(我把左半段标记为 1,右半段标记为 0),删去这一维即可。 接下来就是普通的三维偏序了,唯一的区别在于扫描区间时只有左指针指向标记是 1 的点时才加入数据结构,右指针指向标记是 0 的点时才计算贡献。 数据结构需要维护最大值和对应的方案数,类似[[SDOI2011]拦截导弹](https://www.luogu.com.cn/problem/P2487),最后统计所有答案等于最大值的点的方案数只和即可。 注意一个点可能有多个贡献,要先去重。 ```cpp #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const long long mod=998244353; long long n,m,p[80001],cnt,ans[80001],val[80001],sum,tot; pair<long long,long long> dp[80001]; struct element { long long x,y,z,w,id,val,cnt; bool tag; bool operator ==(const element &other) const { return x==other.x&&y==other.y&&z==other.z&&w==other.w; } }a[80001],b[80001],c[80001]; inline bool cmp1(element x,element y) { return x.x^y.x? x.x<y.x:x.y^y.y? x.y<y.y:x.z^y.z? x.z<y.z:x.w<y.w; } inline bool cmp2(element x,element y) { return x.y^y.y? x.y<y.y:x.z^y.z? x.z<y.z:x.w^y.w? x.w<y.w:x.x<y.x; } inline bool cmp3(element x,element y) { return x.z^y.z? x.z<y.z:x.w^y.w? x.w<y.w:x.x^y.x? x.x<y.x:x.y<y.y; } inline long long read() { long long x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x; } inline int lowbit(int x) { return x&-x; } inline void update(int k,long long p,long long v) { v%=mod; for(;k<=cnt;k+=lowbit(k)) { if(ans[k]==p) val[k]=(val[k]+v)%mod; if(p>ans[k]) { ans[k]=p; val[k]=v; } } } inline pair<long long,long long> query(int k) { pair<long long,long long> res=make_pair(0,0); for(;k;k-=lowbit(k)) { if(ans[k]==res.first) res.second=(res.second+val[k])%mod; if(ans[k]>res.first) { res.first=ans[k]; res.second=val[k]; } } return res; } inline void clear(int k) { for(;k<=cnt;k+=lowbit(k)) ans[k]=val[k]=0; } void cdq2(int l,int r) { if(l==r) return; int mid=(l+r)>>1; cdq2(l,mid); for(register int i=l;i<=r;++i) c[i]=b[i]; sort(c+l,c+mid+1,cmp3); sort(c+mid+1,c+r+1,cmp3); int i=l,j=mid+1; while(j<=r) { if(c[j].tag) { ++j; continue; } while(i<=mid&&c[i].z<=c[j].z) { if(c[i].tag) update(c[i].w,dp[c[i].id].first,dp[c[i].id].second); ++i; } pair<long long,long long> tmp=query(c[j].w); tmp.first+=c[j].val; if(tmp.first==dp[c[j].id].first) dp[c[j].id].second=(dp[c[j].id].second+tmp.second)%mod; if(tmp.first>dp[c[j].id].first) dp[c[j].id]=tmp; sum=max(sum,dp[c[j].id].first); ++j; } for(register int t=l;t<i;++t) if(c[t].tag) clear(c[t].w); cdq2(mid+1,r); } void cdq1(int l,int r) { if(l==r) return; int mid=(l+r)>>1; cdq1(l,mid); for(register int i=l;i<=r;++i) b[i]=a[i]; for(register int i=l;i<=mid;++i) b[i].tag=1; sort(b+l,b+r+1,cmp2); cdq2(l,r); cdq1(mid+1,r); } int main() { n=read(),m=read(); for(register int i=1;i<=n;++i) { a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].w=read(),a[i].val=read(); p[i]=a[i].w; } sort(p+1,p+n+1); cnt=unique(p+1,p+n+1)-p-1; for(register int i=1;i<=n;++i) a[i].w=lower_bound(p+1,p+cnt+1,a[i].w)-p; sort(a+1,a+n+1,cmp1); tot=1; for(register int i=2;i<=n;++i) if(a[i]==a[tot]) a[tot].val+=a[i].val; else a[++tot]=a[i]; n=tot; tot=0; for(register int i=1;i<=n;++i) { a[i].id=i; dp[i].first=a[i].val; dp[i].second=1; } cdq1(1,n); printf("%lld\n",sum); for(register int i=1;i<=n;++i) if(dp[i].first==sum) tot=(tot+dp[i].second)%mod; printf("%lld\n",tot); return 0; } ```