题解:P10690 Fotile 模拟赛 L

· · 题解

wukaichen888 /hs /bx

区间 DP 神仙题。

考虑区间 DP,第一维枚举长度,第二维枚举区间起点,然后就是 DP 转移,设 $dp_{l,r}$ 为 $\max_{l\le i\le j\le r}\bigoplus_{k=i}^{j} a_k$,则这里有一个比较显然的 DP 转移式: $$dp_{l,r}=\max\{a_l\oplus a_{l+1}\oplus\cdots\oplus a_r,dp_{l,r-1},dp_{l+1,r}\}$$ DP 数组的初始值就是 $dp_{i,0}=a_i$。 然后考虑优化这个 $O(n^3)$ 的 DP 转移过程,不难发现异或满足 $x\oplus x=0$,所以 $a_l\oplus a_{l+1}\oplus\cdots\oplus a_r=qzh_r\oplus qzh_{l-1}$,其中 $qzh$ 是前缀异或和数组。所以我们只需要预处理出前缀异或和数组即可把该转移过程优化为 $O(n^2)$。 $$dp_{l,r}=\max\{qzh_r\oplus qzh_{l-1},dp_{l,r-1},dp_{l+1,r}\}$$ 然而,这题的空间不允许你开这么大的数组,所以我们考虑继续优化该 DP。 经过计算,大概空间再减少一半就能开的下了,那么怎么优化掉一半的空间? 不难发现我们的 DP 数组其实有一半都被浪费掉了($r<l$ 的部分),这就是本题的突破口。 C++ 中,有这么一个美妙的东西,它叫 vector 动态数组。 这个 vector 的好处就是它的大小是可以动态调整的,不像普通数组一样定下了就改不了。 但是我们发现浪费的空间并非在数组的末尾,所以我们要更改 DP 式,让浪费的空间出现在数组的末尾。 设 $dp_{l,len}$ 为 $\max_{l\le i\le j\le l+len-1}\bigoplus_{k=i}^{j} a_k$,则这里有一个比较显然的 DP 转移式: $$dp_{l,len}=\max\{qzh_{l+len-1}\oplus qzh_{l-1},dp_{l,len-1},dp_{l+1,len-1}\}$$ DP 数组的初始值就是 $dp_{i,1}=a_i$。 然后我们发现此时被浪费的空间都出现在数组的末尾了,那么如何让这些空间不被浪费? C++ 的 vector 中,有一个函数叫做 resize,它可以控制 vector 的大小,现在这些空间就被节约下来了。 剩下的就是一些细节了,注意 $x+lastans$ 可能会爆 int,要写成 $(x\bmod n+lastans\bmod n)\bmod n$,剩余的同理。 代码: ```cpp #include<bits/stdc++.h> using namespace std; int n,m,x,y,l,r,a[12001],qzh[12001],lastans; vector<int>dp[12001]; int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; dp[i].resize(n-i+2); dp[i][1]=a[i]; qzh[i]=qzh[i-1]^a[i]; } for(int len=2;len<=n;len++){ for(int i=1;i+len-1<=n;i++){ dp[i][len]=max({qzh[i+len-1]^qzh[i-1],dp[i+1][len-1],dp[i][len-1]}); } } while(m--){ cin>>x>>y; l=min(((x%n+lastans%n)%n)+1,((y%n+lastans%n)%n)+1); r=max(((x%n+lastans%n)%n)+1,((y%n+lastans%n)%n)+1); cout<<dp[l][r-l+1]<<'\n'; lastans=dp[l][r-l+1]; } } ```