CodeForces 938B XOR-pyramid
题外话
当我考试时看到这道题时,离考试结束只剩20分钟了。。。
然后,我飞一般地分析了样例,猜出了结论,过了这道题。(想想都觉得惊心动魄。。。手都要起飞了。。。)
题目
题目大意
对于一个长度为 m m 的数组,定义函数:
f(b)={b[1]f(b[1]⨁b[2],b[2]⨁b[3],⋯,b[m−1]⨁b[m])m=1m>1 f ( b ) = { b [ 1 ] m = 1 f ( b [ 1 ] ⨁ b [ 2 ] , b [ 2 ] ⨁ b [ 3 ] , ⋯ , b [ m − 1 ] ⨁ b [ m ] ) m > 1
现给定一个长度为 n n 序列,给出 q q 个询问,对于每个询问,输出给定区间内连续子串的最大函数值。
思路
看题可知,的值非常地大( q≤100000 q ≤ 100000 )而 n n 却非常小(),很容易往离线操作上靠,即用 O(1) O ( 1 ) 的时间复杂度来回答询问。DP是一个不错的选择。
我们定义 f[l][r] f [ l ] [ r ] 为区间 [l,r] [ l , r ] 中 f f 函数值。先来找一下规律:
举个例子吧:(根据样例一绘制)
不难发现,被递归成了
f[l+1][r]⨁f[l][r−1]
f
[
l
+
1
]
[
r
]
⨁
f
[
l
]
[
r
−
1
]
。(画图分析是多么重要)
我们还可以发现边界条件: f[l][r]=a[l](l=r) f [ l ] [ r ] = a [ l ] ( l = r ) 。
若实在不相信这个结论,就请画出更多的图,你会发现这个结论是正确的。
自然,这道题就变成了一道区间DP题。
我们再定义
g[l][r]
g
[
l
]
[
r
]
为区间
[l,r]
[
l
,
r
]
的最大函数值,可得出状态转移方程:
所以,还等什么呢?
正解代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int Maxn=5000;
int N,A[Maxn+5];
int f[Maxn+5][Maxn+5];
int ans[Maxn+5][Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
for(int i=1;i<=N;i++) {
scanf("%d",&A[i]);
ans[i][i]=f[i][i]=A[i];
}
for(int i=2;i<=N;i++)
for(int j=1;j<=N-i+1;j++) {
int k=j+i-1;
f[j][k]=f[j+1][k]^f[j][k-1];
ans[j][k]=max(ans[j+1][k],ans[j][k-1]);
if(f[j][k]>ans[j][k])ans[j][k]=f[j][k];
}
int q;
scanf("%d",&q);
while(q--) {
int l,r;
scanf("%d %d",&l,&r);
printf("%d\n",ans[l][r]);
}
return 0;
}