思路

我线段树属于那种看到题解一般能懂,自己有时候想不到的层次...
这题应该就是一个线段树...
单纯的已知次数下搬运石头肯定是平方答案最优.然后假如不含修改的话,就是一个超级简单.
假如含有修改呢...我们不妨把它放到线段树上进行.
表示为到了这个石头堆(因为线段树是一群一群石头进行维护的嘛~)选了次的最小代价是多少.然后我们愉快的维护这个数组就好了...

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e2+5;
int n,m,q;
ll a[N],f[N<<2][N];
struct Tree{
    int l,r,len;
}tr[N<<2];
ll cost(int n,int m)//n个石子分m次搬运的最小花费.
{
    int num=n/m;
    return 1ll*(m-n%m)*num*num+1ll*(n%m)*(num+1)*(num+1);
}

void pushup(int u)
{
    memset(f[u],0x3f3f3f3f,sizeof f[u]);
    if(tr[u].len==1)
    {
        for(int i=1;i<=m-n+1;i++)
        {
            if(i>=a[tr[u].l]) f[u][i]=a[tr[u].l];
            else
            {
                f[u][i]=cost(a[tr[u].l],i);
            }
        }
        return;
    }
    for(int j=tr[u].len;j<=m-n+tr[u].len;j++)
    {
        for(int k=tr[u<<1].len;k<=j-tr[u<<1|1].len;k++)
        {
            f[u][j]=min(f[u][j],f[u<<1][k]+f[u<<1|1][j-k]);
        }
    }
}

void modify(int u,int pos)
{
    if(tr[u].len==1)
    {
        pushup(u);
        return;
    }
    if(tr[u<<1].r>=pos)
    {
        modify(u<<1,pos);
        pushup(u);
    }
    else
    {
        modify(u<<1|1,pos);
        pushup(u);
    }
}

void build(int u,int l,int r)
{
    tr[u].l=l,tr[u].r=r,tr[u].len=(r-l+1);
    if(l==r)
    {
        for(int i=1;i<=m-n+1;i++)
        {
            if(i>=a[l]) f[u][i]=a[l];
            else
            {
                f[u][i]=cost(a[l],i);
            }
        }
        return;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(u);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)   scanf("%lld",&a[i]);
    build(1,1,n);
    scanf("%d",&q);
    while(q--)
    {
        int x,v;
        scanf("%d%d",&x,&v);
        a[x]=v;
        modify(1,x);
        printf("%lld\n",f[1][m]);
    }
    return 0;
}