最大得分 题解(线段树维护dp)

题目链接

题目思路

大部分人使用的是三维dp,只有我这个fw用的阴间线段树维护dp

首先很容易想到一个\(dp\)

\(dp[i][j]\)表示前\(i\)个元素分为\(j\)个段的最大值

转移方程就是\(dp[i][j]=\max(dp[x][j-1]+gcd(a[x+1],a[x+2]...a[i])) (1\le x \le i-1)\)

答案就是\(dp[n][k]\) 但是这样的复杂度很明显就是\(O(n^2k)\) 妥妥的\(TLE\)

但是你可以将\(gcd(a[x+1],a[x+2]...a[i])\)这些元素分类,最多只有100种,因为\(a[i]\le 100\)

而且你会发现若是固定右端点,枚举左端点的连续那么对于相同\(gcd(a[x+1],a[x+2]...a[i])\)

所对应的位置一定是连续的

\(vec[i][j][0]\)表示以\(a[i]\)为右端点,最左的\(pos\)使得\(\gcd(a[pos],a[pos+1],...a[i])=j\)

\(vec[i][j][1]\)表示以\(a[i]\)为右端点,最右的\(pos\)使得\(\gcd(a[pos],a[pos+1],...a[i])=j\)

然后用线段树维护这个区间的值即可,细节有点多

代码

#include<bits/stdc++.h>
#define debug printf("\n I am here\n");
#define fi first
#define se second
typedef long long ll;
const int maxn=1e4+5,inf=0x3f3f3f3f,mod=1e9+7;
using namespace std;
int n,k;
int a[maxn];
int cal[105][105];
vector<int> vec[maxn][105];
int ma[maxn],mi[maxn];
int tree[maxn<<2][55];
int dp[maxn][55];
int pre[maxn];
void update(int node,int pos,int l,int r,int val,int id){
    if(l==r){
        tree[node][id]=val;
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=pos) update(node<<1,pos,l,mid,val,id);
    else update(node<<1|1,pos,mid+1,r,val,id);
    tree[node][id]=max(tree[node<<1][id],tree[node<<1|1][id]);
}
int query(int node,int L,int R,int l,int r,int id){
    if(L>R) return -inf;
    if(L<=l&&r<=R){
        return tree[node][id];
    }
    int mid=(l+r)/2,ma=-inf;
    if(mid>=L) ma=max(ma,query(node<<1,L,R,l,mid,id));
    if(mid<R) ma=max(ma,query(node<<1|1,L,R,mid+1,r,id));
    return ma;
}
void build(int node,int l,int r){
    for(int i=1;i<=40;i++){
        tree[node][i]=-inf;
    }
    if(l==r){
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
}
int main(){
    scanf("%d%d",&n,&k);
    build(1,1,n);
    for(int i=1;i<=100;i++){
        for(int j=1;j<=100;j++){
            cal[i][j]=__gcd(i,j);
            // cal[i][j]表示i和j的gcd
        }
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=100;j++){
            ma[j]=-inf;
            mi[j]=inf;
        }
        int temp=a[i];
        for(int j=i;j>=1;j--){
            temp=cal[temp][a[j]];
            mi[temp]=min(mi[temp],j);
            ma[temp]=max(ma[temp],j);
        }
        for(int j=1;j<=100;j++){
            if(mi[j]==inf) continue;
            vec[i][j].push_back(mi[j]);
            vec[i][j].push_back(ma[j]);
        }
    }
    for(int i=0;i<=n;i++){
        for(int j=0;j<=50;j++){
            dp[i][j]=-inf;
        }
    }
    for(int i=1;i<=n;i++){
        pre[i]=__gcd(pre[i-1],a[i]);
    }
    // dp[i][j] 表示前i个分为j个段
    for(int i=1;i<=n;i++){
        for(int j=1;j<=min(i,k);j++){
            if(j==1){
                dp[i][j]=pre[i];
            }else{
                for(int u=1;u<=100;u++){
                    if(vec[i][u].size()==0) continue;
                    int x=query(1,max(vec[i][u][0]-1,1),vec[i][u][1]-1,1,n,j-1);
                    dp[i][j]=max(dp[i][j],x+u);
                }
            }
            update(1,i,1,n,dp[i][j],j);
        }
    }
    printf("%d\n",dp[n][k]);
    return 0;
}

posted @ 2021-04-27 23:57  hunxuewangzi  阅读(46)  评论(0编辑  收藏  举报