Codeforces 984D 题解(DP)

题面

传送门
题目大意:
给你一个计算区间f函数的公式,举例f(1,2,4,8)=f(1⊕2,2⊕4,4⊕8)=f(3,6,12)=f(3⊕6,6⊕12)=f(5,10)=f(5⊕10)=f(15)=15 然后现在给你一个数列,n<=5000,然后q个询问,q<=100000,每次询问[l,r]区间内f函数的最大值是多少

分析

此题可用DP求解
d p [ i ] [ j ] 表示区间[i,j]f函数最大值
显然初始值 d p [ i ] [ i ] = a [ i ]
15
5 10
3 6 12
1 2 4 8
我们把题面例子中每个区间的f值写成一个金字塔形
从下到上为1~4行
对于每个区间[l,r],其实我们要统计的是金字塔中的一小部分的最大值
如[1,2],即求金字塔
3
1 2
的最大值,显然是3
显然可以从下到上递推写出
d p [ i ] [ j ] = d p [ i + 1 ] [ j ] x o r d p [ i ] [ j 1 ]
又因为要统计最大值
d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j 1 ] , d p [ i ] [ j [ )
总的状态转移方程为
d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j 1 ] , d p [ i + 1 ] [ j ] x o r d p [ i ] [ j 1 ] )
在代码中分步实现更方便
时间复杂度 O ( n 2 )

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 5005
using namespace std;
int n,q;
int dp[maxn][maxn];
int l,r;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&dp[i][i]);
    for(int i=n;i>=1;i--){
        for(int j=i+1;j<=n;j++){
            dp[i][j]=dp[i][j-1]^dp[i+1][j];
        }
    }
    for(int i=n;i>=1;i--){
        for(int j=i+1;j<=n;j++){
            dp[i][j]=max(dp[i][j],max(dp[i][j-1],dp[i+1][j]));
        }
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%d %d",&l,&r);
        printf("%d\n",dp[l][r]);
    } 
} 

猜你喜欢

转载自blog.csdn.net/oier_forever/article/details/80487723