解析
和诗人小G很像啊
先写出状态转移方程,设f(i)表示前i个玩具可以达到的最小值
f(i)=min{f(j)+val(j+1,i)}其中val(j,i)表示[j,i]区间内的所有的玩具放到一个容器内的时候得到的值
val(j,i)=(i−j+k=j∑iCk−L)2轻松写出一下朴素的n2的算法如下
点击
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
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
int n,L;
int a[maxn];
ll pre_sum[maxn];
ll f[maxn];
ll p[maxn];
ll val(int j,int i) {
ll len = i-(j+1)+pre_sum[i] - pre_sum[j];
len -=L;
return len * len;
}
void init() {
std::cin >> n >> L;
for(int i = 1;i <= n ;++i )
{
std::cin >> a[i];
pre_sum[i] = pre_sum[i-1];
pre_sum[i] += a[i];
}
}
void debug_f() {
cout << "f[i]: ";
for(int i = 1;i <= n ;++i )
{
cout << f[i] << " ";
}
cout << endl;
cout << "p[i]: ";
for(int i = 1;i <= n ;++i )
{
cout << p[i] << " ";
}
cout << endl;
}
int main (int argc, char *argv[]) {
init();
f[0] = 0;
for(int i = 1;i <= n ;++i )
{
f[i] = 0x7f7f7f7f7f7f7f7f;
for(int j = 0 ;j < i;j++)
{
ll t = f[j] + val(j,i);
if( f[i] >= t) {
f[i] = t;
p[i] = j;
}
}
}
cout << f[n] << endl;
#ifdef DEBUG
debug_f();
#endif
return 0;
}
显然要优化的:
决策单调性
这个状态转移方程有决策单调性.下面证明
我们先把题目转化成一个数学问题.
设在一个数轴上有这几个点a<b<x<y
-------a--------b----------x----------y---------
设
- g(a,x) 表示从a转移到x的值
- sum(i) 表示前缀和∑1iCi
- Si=sum(i)+i
- L1=L+1
于是得到g(a,x)
g(a,x)=f(a)+val(a+1,x)=f(a)+(k=a+1∑xCk+x−(a+1)−L)2=f(a)+(sum(x)−sum(a)+x−a−1−L)2=f(a)+(sum(x)+x−(sum(a)+a)−(L+1))2=f(a)+(Sx−Sa−L1)2(1)同理,可以得到g(b,x)
g(b,x)=f(b)+(Sx−Sb−L1)2(2)那么先假定g(b,x)<g(a,x)成立,那么问题变成证明
g(b,x)<g(a,x)⇒g(b,y)<g(a,y)上式恒成立.
同样得到g(a,y),g(b,y)
g(a,y)=f(a)+(Sy−Sa−L1)2(3)g(b,y)=f(b)+(Sy−Sb−L1)2(4)观察发现(3),(4)式对于(1),(2)式来说,只有Sy,Sy不同.根据题意,显然Sy>Sx>0,那么就设Sy=Sx+v,v>0,那么得到新的式子
g(a,y)=f(a)+(Sx+v−Sa−L1)2g(b,y)=f(b)+(Sx+v−Sb−L1)2(5)(6)根据数学直觉,若想知道(5),(6)式的在小关系,那么只需要知道对于(1),(2)式之间的增量之间的大小关系.
g(a,y)=f(a)+(Sx+v−Sa−L1)2=f(a)+(v+(Sx−Sa−L1))2=f(a)+v2+2v(Sx−Sa−L1)+(Sx−Sa−L1)2那么增加的量就是v2+2v(Sx−Sa−L1).同理(6)式的增量就是v2+2v(Sx−Sb−L1).比较两者之间的大小
v2+2v(Sx−Sa−L1)−(v2+2v(Sx−Sb−L1))=2v(Sx−Sa−L1−Sx+Sb−L1)=2v(Sb−Sa)>0因为Sb>Sa一定成立.
所以题目的状态转移方程是满足决策单调性
然后根据可以写出来下的代码
点击
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
int n,L;
int a[maxn];
ll pre_sum[maxn];
ll f[maxn];
struct node {
int p;
int l,r;
};
ll val(int j,int i) {
ll len = i-(j+1)+pre_sum[i] - pre_sum[j];
len -=L;
return len * len;
}
void init() {
std::cin >> n >> L;
for(int i = 1;i <= n ;++i )
{
std::cin >> a[i];
pre_sum[i] = pre_sum[i-1];
pre_sum[i] += a[i];
}
}
template<typename T = int,int siz = maxn>
struct mystack{
T sta[siz+5];
int head = 0;
void clear() { head = 0;}
void push(T a) { sta[head++] = a;}
void pop(){head--;}
T top() { return sta[head-1];}
bool empty() { return head == 0;}
int size() { return head;}
};
mystack<node> sta;
int mid(int l,int r) {
return (l+r) >> 1;
}
template <typename F>
int bs_find(int l,int r,F check) {
while( l < r) {
int m = mid(l,r);
if( check(m))
r = m;
else
l = m+1;
}
return l ;
}
void debug_sta() {
cout << "p[i]: \n";
for(int i = 0 ;i< sta.size();i++) {
cout << "p: " << sta.sta[i].p << " ";
cout << "l: " << sta.sta[i].l << " ";
cout << "r: " << sta.sta[i].r << "\n";
}
cout << endl;
}
void debug_f() {
cout << "f[i]: ";
for(int i = 1;i <= n ;++i )
{
cout << f[i] << " ";
}
cout << endl;
}
int main (int argc, char *argv[]) {
init();
f[0] = 0;
sta.push({0,1,n});
for(int i = 1;i <= n ;++i )
{
#ifdef DEBUG
cout << "before calc i= " << i << endl;
debug_f();
debug_sta();
cout << endl;
#endif
f[i] = 0x7f7f7f7f7f7f7f7f;
int pos = bs_find(0,sta.size(),[i](int m){
return sta.sta[m].r >= i;
});
pos = sta.sta[pos].p;
f[i] = f[pos] + val(pos,i);
while( !sta.empty()) {
node t = sta.top();
ll org = f[t.p] + val(t.p,t.l);
ll now = f[i] +val(i,t.l);
if( now <= org) {
sta.pop();
}
else {
sta.pop();
int pos = bs_find(t.l,t.r+1,[p=t.p,i](int m){
ll org = f[p] + val(p,m);
ll now = f[i] +val(i,m);
return now <= org;
});
if( pos > n){
sta.push(t);
}
else {
sta.push({t.p,t.l,pos-1});
sta.push({i,pos,n});
}
break;
}
}
#ifdef DEBUG
cout << "after calc i= " << i << endl;
debug_f();
debug_sta();
cout << endl;
#endif
}
cout << f[n] << endl;
return 0;
}
slope
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5,maxe = 1e6+5;
int n,L;
using db = long double;
long long sum[maxn];
ll dp[maxn];
struct slope_optimize {
ll k(int i) { return -2 * B(i);}
ll B(int i) {return sum[i] +i -L -1;}
ll A(int j) {return sum[j] +j;}
ll d(int j) {return dp[j] + A(j)*A(j);}
double slope(int i,int j) {
return (d(j) - d(i)) *1.0 / ( A(j) - A(i));
}
ll calc_dp(int i,int j) {
return k(i)*A(j) +d(j)+B(i)*B(i);
}
} slope_opt;
struct myque {
int q[maxn];
int head,tail;
int size() const {
return tail - head;
}
bool empty() const {
return size() == 0;
}
void push(int a) {
q[tail++] = a;
}
void del(int i) {
while( size() > 1 && slope_opt.slope(q[tail-2],i) < slope_opt.slope(q[tail-2], q[tail-1]))
tail--;
}
void update(double v) {
while( size() > 1 && slope_opt.slope(q[head],q[head+1]) < v)
++head;
}
int front() const {
return q[head];
}
int back() const {
return q[tail-1];
}
} que;
void init() {
cin >> n >> L;
for(int i=1;i<=n;++i){
cin >> sum[i];
sum[i] += sum[i-1];
}
}
int main(int argc,char * argv[]){
cin.sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
que.push(0);
for(int i=1;i<=n;i++) {
que.update(- slope_opt.k(i) );
dp[i] = slope_opt.calc_dp(i, que.front());
que.del(i);
que.push(i);
}
cout << dp[n];
return 0;
}