## 非常神仙的 wqs 二分优化dp,又学了一招。

首先我们需要先想到一个人类智慧版的前缀和优化。

 

然鹅在前缀和优化之前我们先考虑暴力做法:
我们可以枚举 i 、 j 令其表示前 i 个村庄设立 j 个邮局的最小贡献。
然后枚举 k 表示前 k 个村庄已经设立邮局,现在处理 k+1~i 的村庄。
接着再枚举当前邮局设立在哪里,然后 O(n) 累加每个村庄的贡献。
这样的复杂度是 O(n^5) 的,也许达不到这个上限,但是 O(n^4) 的时间总是要的。
于是这样...已经炸掉了。

 

# part 2:optimization(human wisdom)


我们考虑在一段区间内建立一个邮局,那么这个邮局会使得附近村庄的贡献降低。
那么如何使得这个降低的贡献最大呢?我们可以由 **~~人类智慧~~ 推论** 得出:
当我们将邮局设立在一个要产生贡献的区间的中点时,降低的贡献最大。
那么这时我们不妨设区间中点坐标为 k ,左端点坐标 i ,右端点坐标 j 。
此时这段区间对答案的贡献为:# $$(S[j]-S[k])-a[k] \times (j-k) + a[k] \times (k-i)-(S[k]-S[i])$$ #

那么这样的复杂度是 O(n^3) 的,已经有了较大进步,起码30分是到手了。

 

# part 3:optimization(Quadrilateral inequality)

于是我们考虑进一步优化,看到  满数据 是 3e3 的数据范围,那么应该是要用 O(n^2) 的算法。
那这里就要用 四边形不等式优化了(我不会)。同学们可以自行研究,大概就是根据 f[i][j] 的一个性质:
f[i][j]+f[i-1][j+1]>f[i-1][j]+f[i][j+1] => f[i][j] 的决策点在 f[i-1][j] 和 f[i][j+1] 之间之类的。
(怎么证我就母鸡了)

于是 O(n^2) 满分。

 

# part4:optimization(wqs binary cut)


然鹅我们还可以考虑进一步升华算法。

我们可以考虑二分将算法复杂度优化成 O(nlogn)

而且是 wqs 二分。

如何二分? 我们考虑给区间的每次分割增加一个贡献。
那么我们可以看出:增加贡献越大,将会分割的次数就越少。
容易想到,当分割出的段数恰好为 m 时,该状态下的 f[n] 减去增加贡献就是答案。
这样一个 log 去了。 那么怎么 O(n) 转移方程?
我们用单调队列优化转移,单调队列内每个点记录上次转移位置以及其控制的后方最优解范围。

也就是说,最后一种算法是用了二分优化 part3 ,将一个 n 变成了 log 。

 

# part 5:coding(s)

 

$$ O(n^3) $$

 1 //by Judge
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<cstdio>
 6 const int M=511;
 7 #ifndef Judge
 8 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 9 #endif
10 char buf[1<<21],*p1=buf,*p2=buf;
11 inline int read(){
12     int x=0,f=1; char c=getchar();
13     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
14     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
15 } int n,m,a[M],s[M],f[M][M];
16 inline void cmin(int& a,int b){ if(a>b) a=b; }
17 int main(){
18     n=read(),m=read(),memset(f,0x3f,sizeof(f)),f[0][0]=0;
19     for(int i=1;i<=n;++i) a[i]=read(); std::sort(a+1,a+1+n);
20     for(int i=1;i<=n;++i) s[i]=s[i-1]+a[i];
21     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int k=0,t;k<i;++k)
22         t=i+k+1>>1,cmin(f[i][j],f[k][j-1]+(s[i]-s[t])-a[t]*(i-t)+a[t]*(t-k)-(s[t]-s[k]));
23     return printf("%d\n",f[n][m]),0;
24 }
n^3

相关文章:

  • 2022-01-01
  • 2022-12-23
  • 2022-02-03
  • 2021-04-13
  • 2021-09-08
  • 2022-01-15
  • 2022-01-26
  • 2022-01-03
猜你喜欢
  • 2021-07-22
  • 2022-12-23
  • 2021-06-11
  • 2022-12-23
  • 2022-12-23
  • 2022-02-12
  • 2022-12-23
相关资源
相似解决方案