hydd的博客

博客

NOIP2021 T3 方差 题解

2021-11-21 12:37:30 By hydd

n¯a=ni=1ain2D=nni=1(ai¯a)2=nni=1a2i2n¯ani=1ai+n2¯a2=nni=1a2i2(ni=1ai)2+(ni=1ai)2=nni=1a2i(ni=1ai)2

观察题目中的式子,aiai1+ai+1ai,根据 CF1110E 的套路,可以差分,令 di=ai+1ai(1i<n),一次操作 (2i<n) 即:

di1=aiai1=(ai1+ai+1ai)ai1=ai+1ai=didi=ai+1ai=ai+1(ai1+ai+1ai)=aiai1=di1

相当于交换 di1,di(2i<n),故 d 可以通过若干次操作,变为任意 d 的排列。

由于 a1 不变,那么 ad 是一一对应的。现在要求一个 d 的排列使得 n2D 最小,继续推式子: n2D=nni=1a2i(ni=1ai)2=nni=1a2ini=1nj=1aiaj=12(nni=1a2i2ni=1nj=1aiaj+nnj=1a2j)=12(ni=1nj=1(aiaj)2)=n1i=1n1j=i(aj+1ai)2=n1i=1n1j=i(di+di+1++dj)2 n2D 取最小值时,d 一定是先递减后递增的。

考虑在分界位置(即递减到递增)从小到大往两边加数,由于 n2D=nni=1a2i(ni=1ai)2 维护 dp[k][s] 表示当前已经加入了 k 个数,现在的 a 之和为 s 的最小 ni=1a2i

初始 dp[1][s]=0

转移时考虑加到左边还是右边:

  • 左边:原来是 a1,a2,,ak,现在变为 d,a1+d,a2+d,,ak+d,新增的贡献为

Δ=d2+ki=1(d+ai)2ki=1a2i=(k+1)d2+2dki=1ai=(k+1)d2+2ds

  • 右边:原来是 a1,a2,,ak,现在变为 a1,a2,,ak,ak+d,新增的贡献为 Δ=(ak+d)2 其实可以发现 ak 是固定的,为之前所有的 d 之和,不需要再记录。

答案为 min

分析一下时间复杂度,第一维是 O(n) 的,第二维是 O(nV) 的,转移 O(1)

但是可以发现 d0 的转移可以忽略,第一维是 O(\min(n,V)) 的,总复杂度为 O(nV^2),可以通过。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
int n,a[11000],d[11000]; ll dp[510000];
ll sqr(ll x){ return x*x;}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<n;i++) d[i]=a[i+1]-a[i];
    sort(d+1,d+n);

    for (int i=0;i<=500000;i++) dp[i]=INF;
    dp[0]=0; int lim=0,sum=0;
    for (int i=1;i<n;i++){
        if (!d[i]) continue;
        for (int s=lim;s>=0;s--){
            if (dp[s]==INF) continue;
            dp[s+sum+d[i]]=min(dp[s+sum+d[i]],dp[s]+sqr(sum+d[i]));
            dp[s+i*d[i]]=min(dp[s+i*d[i]],dp[s]+2*s*d[i]+i*sqr(d[i]));
            dp[s]=INF;
        }
        lim+=i*d[i]; sum+=d[i];
    }
    ll ans=INF;
    for (int i=0;i<=lim;i++)
        if (dp[i]!=INF) ans=min(ans,n*dp[i]-1ll*i*i);
    printf("%lld\n",ans);
    return 0;
}

Comments

No comments yet.

Post a comment

You can use @mike to mention the user mike.

If you wish to input the '@' character, please use @@