题解:
相当于给一个
ai={1,−1}
a
i
=
{
1
,
−
1
}
的序列,划分为
m
m
段使得每段绝对值最小。
记 为序列之和。
容易发现答案为
|sum|m
|
s
u
m
|
m
。
注意当
|sum|=0
|
s
u
m
|
=
0
时有特殊情况。 我们记
si=∑ni=iai
s
i
=
∑
i
=
i
n
a
i
,下面证明这个结论。
1.当
|sum|=0
|
s
u
m
|
=
0
,且
si+1=0
s
i
+
1
=
0
的 位置
≥m
≥
m
,此时我们直接取
m
m
个位置即可。
2.当,且
si+1=0
s
i
+
1
=
0
的位置
<m
<
m
, 此时显然答案为
1
1
。
3.当,此时答案为 |sum|m | s u m | m 。
证明:
如果
|sum|≥m
|
s
u
m
|
≥
m
,把相邻的
−1,1
−
1
,
1
合并,最后剩下一些
1
1
或者,分为
m
m
组。
否则情况变为。
然后 ans=0 a n s = 0 的情况直接特判做单调队列。
(以下除法取上整)
对于
ans≠0
a
n
s
≠
0
的情况, 我们仍然采用贪心的方法,首先维护当前的可选集合,记当前序列总和为
sum
s
u
m
,一个位置
i
i
可选当且仅当:
1.
2.
|si+1|m−1≤ans
|
s
i
+
1
|
m
−
1
≤
a
n
s
3.
n−k≥m−1
n
−
k
≥
m
−
1
。
前两个条件只跟 si+1 s i + 1 有关且为一段区间,我们可以每次把满足 3 3 的位置放入平衡树然后查最值。
不过这样不够优雅, 我们进一步发现可以暴力枚举的取值,因为 O(ans∗m)=O(S) O ( a n s ∗ m ) = O ( S ) , 对于每个值维护一个单调队列表示可选的数,每次贪心取最大即可。 时间复杂度 O(S) O ( S ) 。
#include <bits/stdc++.h>
using namespace std;
const int RLEN=1<<18|1;
inline char nc() {
static char ibuf[RLEN],*ib,*ob;
(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
char ch=nc(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
return i*f;
}
char obuf[RLEN],*wt=obuf;
inline void print(char c) {
(wt==obuf+RLEN) && (fwrite(obuf,1,RLEN,stdout), wt=obuf);
*wt++=c;
}
inline void W(int x) {
static int buf[50];
if(!x) {print('0'); return;}
if(x<0) {print('-'); x=-x;}
while(x) {buf[++buf[0]]=x%10; x/=10;}
while(buf[0]) print(buf[buf[0]--]+'0');
}
const int N=1e6+50, B=5e5+25, INF=0x3f3f3f3f;
struct node {
node *pre,*suf;
int val;
node():pre(NULL),suf(NULL){}
}Pool[N],*pool=Pool;
inline node* newnode(int v) {
++pool; pool->val=v;
return pool;
}
struct List {
node *bg,*ed;
List():bg(NULL),ed(NULL){}
inline bool empty() {return !bg;}
inline int back() {return ed->val;}
inline void pop_back() {
node *t=ed;
if(t==bg) {
bg=ed=NULL;
} else {
ed=t->pre;
t->pre->suf=NULL;
t->pre=NULL;
}
}
inline void push_back(int v) {
node *t = newnode(v);
if(!bg) {
bg=ed=t;
} else {
t->pre=ed;
ed->suf=t;
ed=t;
}
t->val=v;
}
inline int front() {return bg->val;}
inline void pop_front() {
node *t=bg;
if(t==ed) {
bg=ed=NULL;
} else {
bg=t->suf;
t->suf->pre=NULL;
t->suf=NULL;
}
}
};
List que[N];
int n,m,ans,c0,p[B],d[B],s[B],cnt[B];
inline int ceil(int x,int y) {
return (x%y) ? (x/y+1) : (x/y);
}
inline void inc(int id,int pos,int pi) {
while(!que[id].empty() && p[que[id].back()]>pi) que[id].pop_back();
que[id].push_back(pos);
}
int main() {
n=rd(), m=rd();
for(int i=1;i<=n;i++) p[i]=rd(), d[i]=(rd()==1) ? 1 : -1;
for(int i=n;i>=1;i--) s[i]=d[i]+s[i+1],c0+=(!s[i+1]);
ans=s[1] ? (ceil(abs(s[1]),m)) : ((c0>=m) ? 0 : 1);
if(m==1) {printf("%d\n",p[n]); return 0;}
if(!ans) {
for(int i=n;i;i--) cnt[i]=(!s[i+1])+cnt[i+1];
int sze=0;
for(int i=1;i<=n;i++) {
if(s[i+1]) continue;
while(!que[1].empty() && p[que[1].back()]>p[i] && cnt[i]+sze>m) que[1].pop_back(), --sze;
que[1].push_back(i); ++sze;
}
while(!que[1].empty()) W(p[que[1].front()]), print(' '), que[1].pop_front();
} else {
s[n+1]=INF;
for(int i=1;i<=n-m+1;i++) inc(s[i+1]+B,i,p[i]);
int sum=s[1], last=0;
for(int i=1;i<m;i++) {
int v=INF, id;
for(int j=sum-ans+B;j<=sum+ans+B;++j) {
while(!que[j].empty() && que[j].front()<last) que[j].pop_front();
if(que[j].empty() || ceil(abs(j-B),m-i)>ans) continue;
if(p[que[j].front()]<v) v=p[que[j].front()], id=j;
}
last=que[id].front();
W(v); print(' ');
sum=s[last+1];
que[id].pop_front();
if(i!=m-1) inc(s[n-m+i+2]+B,n-m+i+1,p[n-m+i+1]);
}
W(p[n]); print('\n');
}
fwrite(obuf,1,wt-obuf,stdout);
}