题意

堆石头 牛牛每次都能选择其中一堆然后将其中的石头搬走 如果一次搬运的石子数量是那么这堆石头讲牛牛产生的的负担 然后牛牛最多只能搬

然后牛能会进行次操作 每一次操作都会改变一堆石子的数量 然后让我们求牛能每一次操作之后 牛牛的最小负担

首先我们不看牛能的操作 很明显对于求牛牛的负担就是一个的过程

因为产生的负担 因此可以知道 如果我们要用次去搬运个石头 那么最好的做法就是将个石头平均分成份 很容易就可以知道吧

如果有不能平分的也很简单 就将多余的每一个组放一个 到放完为止

然后其实后面也就不难了 我们将放在树上 最小面的叶子节点就是直接 然后往上维护的时候就是去枚举子节点的值

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
const ll MOD = 1e9 + 7;

const int N = 400 + 7;
const int INF = 0x3f3f3f3f;

#define mid ((l + r) >> 1)
#define lson rt << 1 , l , mid
#define rson rt << 1 | 1, mid + 1 , r

ll n , m , q , x , v;
ll dp[N << 2][N] , a[N];

ll calc(ll x , int p){
    ll num = x / p ;
    ll cnt1 = x % p ;
    ll cnt2 = p - cnt1;
    return cnt2 * num * num + cnt1 * (num + 1) * (num + 1);
}

void push_up(int rt){
    memset(dp[rt] , 0x3f , sizeof(dp[rt])) ;
    for(int i = 1 ; i <= m ; ++i){
        for(int j = 1 ; i + j <= m ; ++j){
            dp[rt][i + j] = min(dp[rt][i + j] , dp[rt << 1][i] + dp[rt << 1 | 1][j]);
        }
    }
}

void build(int rt , int l , int r){
    if(l == r){
        for(int i = 1 ; i <= m ; ++i){
            dp[rt][i] = calc(a[l] , i);
        }
        return ;
    }
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int rt , int l , int r , int pos){
    if(l == r){
        for(int i = 1 ; i <= m ; ++i){
            dp[rt][i] = calc(a[l] , i);
        }
        return ;
    }
    if(pos <= mid)  update(lson , pos);
    else update(rson , pos);
    push_up(rt); 
}

void solve(){
    scanf("%lld%lld" , &n , &m);
    for(int i = 1 ; i <= n ; ++i)
        scanf("%lld" , &a[i]);
    build(1 , 1 , n);
    int T;
    scanf("%d" , &T);
    while(T--){
        scanf("%lld%lld" , &x , &v);
        a[x] = v;
        update(1 , 1 , n , x);
        printf("%lld\n" , dp[1][m]);
    }
}

int main(void){
    solve();

    return 0;
}