【Foreign】划分序列 [线段树][DP]
划分序列
Time Limit: 20 Sec Memory Limit: 256 MBDescription
Input
Output
仅一行一个整数表示答案。
Sample Input
9 4
1 1 1 3 2 2 1 3 1
Sample Output
5
HINT
Main idea
将序列分为若干段,使得和最大的那一段最小,值可以为负。
Source
首先,显然都想到了二分答案。
我们先把都为正数或负数的情况写了:Ai>=0的时候求出最小的划分段数x,若x<=K则表示当前答案可行;Ai<=0的时候求出最大的划分段数x,若x>=K则表示当前答案可行。然后再打了暴力,接着我们对拍一下,惊讶地发现了一个规律:若最小划分段数为L,最大划分段数为R,当L<=K<=R时则可以更新。
然后我们用DP来求L和R,也就是:若一段的和满足<=mid,则可以分为一段。
然后我们发现,可以用线段树优化寻找1~i-1中的最小值或最大值,这样判断就可以满足效率了。
Code
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cmath>
7 #include<vector>
8 using namespace std;
9 typedef long long s64;
10
11 const int INF = 2147483640;
12 const int ONE = 5e4+5;
13
14 int n,block;
15 int L,R;
16 int x,sum[ONE],s[ONE];
17 int li[ONE],li_num;
18 int f_min[ONE],f_max[ONE];
19 int res_min,res_max;
20 int Zero;
21
22 int get()
23 {
24 int res=1,Q=1; char c;
25 while( (c=getchar())<48 || c>57)
26 if(c=='-')Q=-1;
27 if(Q) res=c-48;
28 while((c=getchar())>=48 && c<=57)
29 res=res*10+c-48;
30 return res*Q;
31 }
32
33 namespace Seg
34 {
35 struct power
36 {
37 int minn;
38 int maxx;
39 }Node[ONE];
40
41 void Build(int i,int l,int r)
42 {
43 Node[i].minn = INF;
44 Node[i].maxx = -INF;
45 if(l==r) return;
46 int mid=(l+r)>>1;
47 Build(i<<1,l,mid); Build(i<<1|1,mid+1,r);
48 }
49
50 void Update(int i,int l,int r,int L,int x,int PD)
51 {
52 if(l==r)
53 {
54 if(!PD) Node[i].minn = x;
55 else Node[i].maxx = x;
56 return;
57 }
58 int mid=(l+r)>>1;
59 if(L<=mid) Update(i<<1,l,mid,L,x,PD);
60 else Update(i<<1|1,mid+1,r,L,x,PD);
61 Node[i].minn = min(Node[i<<1].minn, Node[i<<1|1].minn);
62 Node[i].maxx = max(Node[i<<1].maxx, Node[i<<1|1].maxx);
63 }
64
65 void Query(int i,int l,int r,int L,int R)
66 {
67 if(L<=l && r<=R)
68 {
69 res_min=min(res_min, Node[i].minn);
70 res_max=max(res_max, Node[i].maxx);
71 return;
72 }
73 int mid=(l+r)>>1;
74 if(L<=mid) Query(i<<1,l,mid,L,R);
75 if(mid+1<=R) Query(i<<1|1,mid+1,r,L,R);
76 }
77 }
78
79 int Check(int mid)
80 {
81 Seg::Build(1,1,li_num);
82 Seg::Update(1,1,li_num, Zero,0,0);
83 Seg::Update(1,1,li_num, Zero,0,1);
84 for(int i=1;i<=n;i++)
85 {
86 int pos = lower_bound(li+1,li+li_num+1,sum[i] - mid) - li;
87 res_min = INF; res_max = -INF; Seg::Query(1,1,li_num, 1,pos);
88 f_min[i] = res_min + 1;
89 f_max[i] = res_max + 1;
90 Seg::Update(1,1,li_num, s[i],f_min[i],0);
91 Seg::Update(1,1,li_num, s[i],f_max[i],1);
92 }
93 return (f_min[n]<=block && block<=f_max[n]);
94 }
95
96
97 int main()
98 {
99 n=get(); block=get();
100 li[++li_num] = 0;
101 for(int i=1;i<=n;i++)
102 {
103 x=get();
104 li[++li_num] = sum[i] = sum[i-1] + x;
105 if(x < 0) L+=x; else R+=x;
106 }
107
108 sort(li+1,li+li_num+1);
109 li_num = unique(li+1,li+li_num+1) - li - 1;
110
111 for(int i=1;i<=n;i++)
112 s[i]=lower_bound(li+1,li+li_num+1, sum[i]) - li;
113 Zero = lower_bound(li+1,li+li_num+1, 0) - li;
114
115 while(L < R - 1)
116 {
117 int mid=(L+R)>>1;
118 if(Check(mid)) R = mid;
119 else L = mid;
120 }
121
122 if(Check(L)) printf("%d",L);
123 else printf("%d",R);
124 }