APIO2014 序列分割

传送门

k个划分点

显然对于给定的一组划分点,任意两个不在同一个块的i,ji,j都会对答案产生AiAjA_iA_j的贡献,就可以不用考虑划分点的顺序了。

由于k200k\le 200,可以直接k次斜率优化DPO(nk)O(nk)解决,但是这样不够优美!要把k200k\le 200去掉就过不了了!

由于k越大答案越大,我们还可以通过凸优化解决。

设一个mid,对于每一次划分都要付出mid的代价,然后直接斜率优化DP最大答案,并且记录达到这个dp值的最小划分块数。随着mid增加,最小划分块数会不增。

找到最大满足最小划分块数<=k+1的mid。这个显然可以二分。

然后dp以后输出dp值+k*mid就可以解决不要输出方案的问题了。

但是方案是真的难输出

记录前i个元素,划分使得答案为dp[i]划分次数的最小值和最大值。以及能得到dp[i] 以i为右端点块的所有左端点。

然后对于Ai=0A_i=0要特别考虑。

比较难调。

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
/*
Author: CNYALI_LK
LANG: C++
PROG: 3648.cpp
Mail: cnyalilk@vip.qq.com
*/
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %lld\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
#define x first
#define y second
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
//typedef long long ll;
typedef long long ll;
typedef pair<ll,ll> pii;
template<class T>ll chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>ll chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
template<class T>ll dcmp(T a,T b){return a>b;}
template<ll *a>ll cmp_a(ll x,ll y){return a[x]<a[y];}
#define min mmin
#define max mmax
#define abs aabs
ll read(){
ll s=0,base=1;
char c;
while(!isdigit(c=getchar()))if(c=='-')base=-base;
while(isdigit(c)){s=s*10+(c^48);c=getchar();}
return s*base;
}
ll a[102423],s[102423],f[102423],g[102423],n,mn[102423],mx[102423];
vector<ll> lst[102423];
pii q[102423],*l,*r;
void Top(ll w,ll bt){
while(l<r && ((l+1)->y-l->y)<w*(s[(l+1)->x]-s[l->x]))++l;
f[bt]=s[l->x]*w-l->y;
mn[bt]=mn[l->x]+1;
lst[bt].clear();
while(l<r && ((l+1)->y-l->y)==w*(s[(l+1)->x]-s[l->x])){
lst[bt].push_back(l->x);
++l;
}
mx[bt]=mx[l->x]+1;
lst[bt].push_back(l->x);
}
void Push(pii a){
while(l<r && (a.y-r->y)*(s[a.x]-s[(r-1)->x])<(a.y-(r-1)->y)*(s[a.x]-s[r->x]))--r;
*(++r)=a;
}
ll dp(ll w){
l=r=q;
pii *Xans;
for(ll i=1;i<=n;++i){
if(!a[i]){
mn[i]=mn[i-1];
if(!w){
mx[i]=mx[i-1]+1;
lst[i].clear();
lst[i].push_back(i-1);
}
else{
mx[i]=mx[i-1];
lst[i].clear();
}
f[i]=f[i-1];
}else{
Push(make_pair(i-1,s[i-1]*s[i-1]-f[i-1]));
Top(s[i],i);
f[i]-=w;
}
}
return mn[n];
}
void Out(ll x,ll times,ll out){
while(x && !a[x] && mx[x-1]>=times)--x;
if(!x)return;
--times;
for(auto i:lst[x]){
if(mn[i]<=times && times<=mx[i]){
Out(i,times,1);break;
}
}
if(out)printf("%lld ",x);
}
int main(){
#ifdef cnyali_lk
freopen("3648.in","r",stdin);
freopen("3648.out","w",stdout);
#endif
ll k,l,r,mid;
n=read();
k=read()+1;
for(ll i=1;i<=n;++i){
a[i]=read();
s[i]=s[i-1]+a[i];
}//*
l=0,r=1e18;
while(l<=r){
mid=(l+r)>>1;
if(dp(mid)>k)l=mid+1;
else r=mid-1;
}
//*/
mid=r+1;
dp(mid);
printf("%lld\n",f[n]+mid*k);
Out(n,k,0);
putchar('\n');
return 0;
}
文章目录
|

博客使用Disqus作为评论系统