题解--探险者笔记III

· · 题解

关于探险者笔记和探险者笔记II,第一个出 R1 的时候被拒了,第二个重了,总之就是SPFA了

20pts

题目给的信息非常难看,我们需要将其简化。我们设 c_i=\sum\limits_{k=1}^{sum_i}b_{a_{i_k}},这很明显是个定值。然后我们再把第 i 个成就需要的关卡用一个二进制数表示出来,设其为 p_i。我们便可以推出一个 O(m^2) 的 dp:

直接暴力转移即可。 ### 50~60pts 上面那个式子本质上是个三维偏序,考虑用 cdq 分治加速这个转移。 只需用 cdq 解决 $c_j+w\ge c_i$,然后暴力枚举子集转移 $p_j\in p_i$ 即可。 时间复杂度 $O(2^nm\log m)$。 ## 100pts 暴力枚举子集时间复杂度显然不对,我们考虑怎么优化枚举。这时就有两种方法: 一种是修改时把 $p_j$ 存下来,查询时枚举 $p_i$ 的子集,这样修改 $O(1)$,查询 $O(2^n)$。 另一种是修改时枚举所有 $p_j$ 的超集进行预处理,查询时直接在数组中查询,这样修改 $O(2^n)$,查询 $O(1)$。 我们考虑如何平衡这个复杂度。我们可以把一个 $18$ 位的二进制数劈成前 $9$ 位和后 $9$ 位。然后定义一个二维数组 $g_{s,t}$ 表示 $p_i$ 前 $9$ 位为 $s$,$p_j$ 后 $9$ 位为 $t$ 时的最优决策。 在修改时,我们枚举 $p_j$ 前 $9$ 位的超集,然后把它的后 $9$ 位不枚举,直接存起来。 在查询时,我们就直接调用 $p_i$ 的前 $9$ 位,再枚举其后 $9$ 位即可。 这样就达到了平衡复杂度的目标,总时间复杂度 $O(2^{\frac{n}{2}}m\log m)$,可以通过本题。 ```cpp #include<bits/stdc++.h> using namespace std; const int maxn=1e5+5,maxm=512; struct node { int a,b,v,id; friend bool operator<(node a,node b) { return a.a==b.a?a.id<b.id:a.a>b.a; } } p[maxn],zc[maxn]; int g[maxm][maxm],n,f[maxn],b[maxn],m,w; void add(int x,int v) {//修改 int gw=x>>9,dw=x&511; if(n<=9) { g[0][dw]=max(g[0][dw],v);//如果只有8位,修改就不用枚举,减小常数。 return; } for(register int i=gw;; i=(i+1)|gw) {//枚举前8位的超集 g[i][dw]=max(g[i][dw],v);//后8位直接储存 if(i==511)break; } } void clr(int x) {//清空,道理同修改 int gw=x>>9,dw=x&511; if(n<=9) { g[0][dw]=0; return; } for(register int i=gw;; i=(i+1)|gw) { g[i][dw]=0; if(i==511)break; } } int ask(int x) { int gw=x>>9,dw=x&511,sum=0; for(register int i=dw;; i=(i-1)&dw) {//查询时枚举后8位 sum=max(sum,g[gw][i]); if(!i)break; } return sum; } void cdq(int l,int r) { if(l==r)return; int mid=(l+r)/2,lst=l; cdq(l,mid); sort(p+l,p+mid+1),sort(p+mid+1,p+r+1); for(register int i=mid+1; i<=r; i++) { while(p[lst].a>=p[i].a-w&&lst<=mid) { add(p[lst].b,f[p[lst].id]); lst++; } f[p[i].id]=max(f[p[i].id],ask(p[i].b)+p[i].v); } for(register int i=l; i<lst; i++)clr(p[i].b); for(register int i=l; i<=r; i++)zc[p[i].id]=p[i]; for(register int i=l; i<=r; i++)p[i]=zc[i]; cdq(mid+1,r); }//注意cdq优化dp必须按中序遍历 int main() { int siz,x; scanf("%d%d%d",&n,&m,&w); for(register int i=1; i<=n; i++)scanf("%d",&b[i]); for(register int i=1; i<=m; i++) { scanf("%d%d",&p[i].v,&siz); for(register int j=1; j<=siz; j++) { scanf("%d",&x); p[i].a+=b[x],p[i].b|=1<<x-1; } p[i].id=i,f[i]=p[i].v; } cdq(1,m); int ans=0; for(register int i=1; i<=m; i++)ans=max(ans,f[i]); printf("%d\n",ans); return 0; } ```