zoukankan      html  css  js  c++  java
  • 7012. 2021.03.15【2021省赛模拟】十

    一个无限长的01串上,每轮操作:选个最靠左的没有操作过的1,把它和右边最近的0交换,重复操作直到找不到这个1为止。

    问操作了无限轮之后1的各个连续段的长度。

    (nle 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;
    }
    
  • 相关阅读:
    SQL中内连接和外连接
    MySQL执行计划解读
    排序算法
    Edge浏览器安装sci-hub插件及使用教程
    MATLAB R2020B 使用教学——窗口布局设置
    PHP半年了,已经可以独立支撑项目,几点心得记录
    看1000行代码不如自己写10行代码
    PHP逻辑运算符中的and和&&以及or和||是有区别的
    自学PHP的野方法
    PHP中SQL查询语句的id=%d解释
  • 原文地址:https://www.cnblogs.com/jz-597/p/14545686.html
Copyright © 2011-2022 走看看