7012. 2021.03.15【2021省赛模拟】十
一个无限长的01串上,每轮操作:选个最靠左的没有操作过的1,把它和右边最近的0交换,重复操作直到找不到这个1为止。
问操作了无限轮之后1的各个连续段的长度。
\(n\le 10^6\)
可以看做这样的操作:有个变量\(s\),从左到右扫,见到1之后加1,见到0之后如果\(s>0\)则减1并在当前位置放个1。
更加抽象:把1看成左括号,要放些右括号跟它们匹配,要求右括号尽量往左边靠。
显然一轮操作前后,前面的右括号是后面的左括号。
有结论:设\(a_i\)表示层数为\(i\)的括号对的个数。其中层数为\(i\)的括号对定义为:()
是层数为\(1\)的括号对,层数为\(i\)的括号对中间包含的括号对层数最多为\(i-1\)(即从里往外)。那么一轮操作之后\(a\)不变。
证明:
首先后一轮可以看做固定左括号选右括号,前一轮可以看做固定右括号选左括号(很显然但不会证)。
那么可以看做:有一堆特殊点,作为左括号搞,和作为右括号搞,\(a\)是一样的。
以作为左括号搞为例。求出\(a_i\)的方法:每次同时删去一行中所有10。第\(i\)次做删去的个数就是\(a_i\)。
把0和1压起来看成连续段。以左括号搞为例时,相当于除了最左边的无限段外,所有连续段长度减一;以右括号搞为例时,相当于除了最右边的无限段外,所有连续段长度减一。无限连续段减一没有区别,所以这样操作可以看做是等价的。
根据上面的证明,我们也知道了\(a_i\)的求法。
显然最后各个连续段的长度是不下降的。贪心地分配即可。
可以用set
维护。每次取出长度最小的连续段来搞。细节不说。
using namespace std;
#include <bits/stdc++.h>
#define N 1000005
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define ll long long
#define INF 1000000000000000
#define MX 10000000
int input(){
char ch=getchar();
while (ch<'0' || ch>'9')
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n;
ll a[N];
int pre[N],suc[N];
set<pair<ll,int> > s;
pair<ll,int> p[N];
int cnt;
ll g[N];
int main(){
freopen("one.in","r",stdin);
freopen("one.out","w",stdout);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
n=input();
for (int i=0;i<=n;++i)
a[i]=input();
a[n+1]=INF;
for (int i=0;i<=n+1;++i)
pre[i]=i-1,suc[i]=i+1;
for (int i=0;i<=n;i+=2)
s.insert(mp(min(a[i],a[i+1]),i));
ll c=n/2+1,tag=0;
while (!s.empty()){
int x=s.begin()->se;
s.erase(s.begin());
ll v=min(a[x],a[suc[x]])-tag;
p[++cnt]=mp(tag+v,c);
tag+=v;
c--;
int y=suc[x];
if (a[x]-tag){
int z=suc[y];
s.erase(mp(min(a[z],a[suc[z]]),z));
a[z]+=a[x]-tag;
s.insert(mp(min(a[z],a[suc[z]]),z));
}
else{
int z=pre[x];
if (z!=-1){
s.erase(mp(min(a[pre[z]],a[z]),pre[z]));
a[z]+=a[y]-tag;
s.insert(mp(min(a[pre[z]],a[z]),pre[z]));
}
}
pre[suc[y]]=pre[x];
if (pre[x]!=-1)
suc[pre[x]]=suc[y];
}
for (int i=cnt;i>=1;--i)
g[p[i].se]+=p[i].fi-p[i-1].fi;
for (int i=n;i>=1;--i)
g[i]+=g[i+1];
for (int i=n;i>=1;--i)
if (g[i])
printf("%lld ",g[i]);
return 0;
}