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

京公网安备 11010502036488号