codechef Chef and Bitwise OR Operation(dp分治优化)

首先,列出O(n^3)的转移方程
dp[i][j]=max(dp[k][j-1]+f(k+1,i)),f(k+1,i)表示区间[k+1,i]的或和。
因为是或运算,所以把一段分成两段,两段的价值一定大于等于一段的价值,所以dp满足
dp[i][j]>=dp[i][j-1],也就是dp[i][j]>=dp[k][j-1]+f(k+1,i)。
又因为或运算只会增大不会减少,所以也有dp[i][j]>=dp[i-1][j],就相当于在dp[i-1][j]的末尾区间在或上一个a[i],所以结论显然。
于是可以证明决策单调。
我们假设dp[i][j]的决策点是k,那么对于dp[s][j](s>i),dp[s][j]的决策点是h,都有h>=k。
证明很简单: 我们假设dp[s][j]用k前面的决策点进行优化,显然不会比用k来进行优化更优,因为dp[s][j]>=dp[i][j],而dp[i][j]已经证明了最优决策点是k,所以dp[s][j]的用k来优化会优。
而对于大于k的,我们有dp[h][j-1]>=dp[k]j-1,由于dp[h][j-1]是逐渐变大的,而f(h+1,s)<=f(k+1,s),当
dp[h][i-1]逐渐增大而f(h+1,s)不变时,dp[s][i]就会更大,所以大于等于k的区间都有可能使dp[s][j]的决策点。
我们用分治优化
设dp[l,r]表示dp[l][j],dp[l+1][j]…dp[r-1][j],dp[r][j]
假设dp[l,r]的最优决策点在区间[L,R]内,设mid为区间l,r的中点,dp[mid][j]的最优决策点是k,由于决策单调,有:
dp[l,mid-1]的最优决策点一定在[L,k]内
dp[mid+1,r]的最优决策点一定在[k+1,R]内
因此对dp进行分治,时间复杂度O(nk*log2(n))

#include 
using namespace std;
typedef long long ll;
const int N=5005;
ll dp[N][N];
int a[N],f[N][N];
int cal(int k,int x,int lb,int rb)
{
    int ans=lb;
    dp[x][k]=-1;
    for(int i=lb;i<=min(rb,x-1);i++)
    {
        ll s=dp[i][k-1]+f[i+1][x];
        if(s>dp[x][k])
        {
            dp[x][k]=s;
            ans=i;
        }
    }
    return ans;
}
void solve(int k,int l,int r,int lb,int rb)
{
    if(l>r) return;
    if(l==r){cal(k,l,lb,rb);return;}
    int m=l+r>>1;
    int ans=cal(k,m,lb,rb);
    solve(k,l,m-1,lb,ans);
    solve(k,m+1,r,ans,rb);
}
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++)
        {
            f[i][i]=a[i];
            for(int j=i+1;j<=n;j++)
                f[i][j]=f[i][j-1]|a[j];
        }
        for(int i=1;i<=n;i++)
            dp[i][1]=f[1][i];
        for(int i=2;i<=k;i++)
            solve(i,i,n-k+i,i-1,n-k+i-1);
        cout<

你可能感兴趣的:(codechef Chef and Bitwise OR Operation(dp分治优化))