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;
}
posted @ 2021-03-16 20:31  jz_597  阅读(80)  评论(0编辑  收藏  举报