「bsoj3425」分班 - 动态规划+单调队列 | Bill Yang's Blog
0%

「bsoj3425」分班 - 动态规划+单调队列

题目大意

题目分析

根据题目我们可以写出状态转移方程:
设$f[i,j]$表示将前$j$个学生分到前$i$个班的最小评价指数。

其中$x[]$为平方前缀和,$s[]$为前缀和。

那么我们可以用$O(nm^2)$的时间完成,这显然要超时,考虑优化。

将上述式子化简,得到:

发现$j$与$k$分离无影响,因为这是2D/1D的动规,故$i$可以在转移式中无视掉。

故我们将$-x[k]-k\times ave^2+2ave\times s[k]$记为$tmp[k]$,则式子化简为:

我们可以事先求出$tmp[]$,然后采用单调队列优化该Dp方程。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
LL t,n,m,A,B,x[10005],f[2][10005],g[2][10005],G[205],s[10005],tmp[10005],ave;
int main() {
t=Get_Int();
while(t--) {
m=Get_Int();
n=Get_Int();
A=Get_Int();
B=Get_Int();
ave=0;
for(int i=1; i<=m; i++) {
LL tmp=Get_Int();
x[i]=x[i-1]+tmp*tmp;
s[i]=s[i-1]+tmp;
ave+=tmp;
}
ave=ave/m;
for(int i=1; i<=n; i++)G[i]=Get_Int();
for(int j=0; j<=m; j++)f[0][j]=f[1][j]=1e17,tmp[j]=-x[j]-j*ave*ave+2*s[j]*ave;;
f[0][0]=0;
LL ans=1e17;
int ansl,ans2;
for(int i=1; i<=n; i++) {
deque<int>Q;
for(int j=i*A; j<=min(m,i*B); j++) {
while(!Q.empty()&&Q.front()<j-B)Q.pop_front();
while(!Q.empty()&&f[(i-1)&1][Q.back()]+tmp[Q.back()]*G[i]>=f[(i-1)&1][j-A]+tmp[j-A]*G[i])Q.pop_back();
Q.push_back(j-A);
if(!Q.empty())f[i&1][j]=f[(i-1)&1][Q.front()]+(tmp[Q.front()]+x[j]+j*ave*ave-2*s[j]*ave)*G[i],g[i&1][j]=j-Q.front();
}
if(f[i&1][m]<ans) {
ans=f[i&1][m];
ansl=i;
ans2=g[i&1][m];
}
}
printf("%lld %d %d\n",ans,ansl,ans2);
}
return 0;
}
姥爷们赏瓶冰阔落吧~