YACS-2024年4月-乙组-T1

题目

题目描述

给定一个只由 \(0\)\(1\) 构成序列,不断扫描序列,在每一轮扫描的过程中,如果发现有一些 \(1\)\(0\) 相邻,且 \(1\) 在前,\(0\) 在后,就在这一轮扫描后,同时将这些 \(1\) 与相邻的 \(0\) 交换。不断进行调整直到将所有的 \(0\) 都在序列的前一半,所有的 \(1\) 都在序列的后一半为止。
请计算需要进行多少轮交换才能完成调整。

输入

若干 \(0\)\(1\) 字符组成的一个序列

输出

单个整数:表示交换的次数。

数据范围

\(1\le\) 序列的长度 \(\le300,000\)

样例数据
输入: 1100
输出: 3

赛时代码 \((60p,40pTLE)\)

#include<bits/stdc++.h>
using namespace std;
int main()
{
	string s;
	bool flag=0;
	int cnt=0;
	cin>>s;
	while(!flag) 
	{
		bool f=1,pos[300005]={};
		for(int i=1;i<s.size();i++)
		{
			if(s[i-1]=='1'&&s[i]=='0')
			{
				pos[i]=true;
				f=0;
			}
		}
		for(int i=1;i<s.size();i++)
		{
			if(pos[i]==1)
			{
				s[i]='1';
				s[i-1]='0';
			}
		}
		if(!f) cnt++;
		else flag=1;
	}
	cout<<cnt;
	return 0;
}

正解思路:

该题其实是逆序对数的一个特殊情况。对每个 \(s_i=1\),其交换次数就是 \(i\) 右侧 0的个数(不妨记为 \(c_i\)),则
\(ans=\sum_{s_i=1}c_i\)

本题中考虑每一轮是并行的,粗略看答案大概是 \(\max_{1...n} c_i\)
⁡这种形式。但关键是会出现 11 这种情况,导致左边的 1 不得不若干轮,而等的情况可能是级联产生的,具体过程可能很复杂,如 10111011。所以不能往这个方向去想,而应该抓住一些整体的量

对某个 \(s_i=1\),记 \(nxt_i\)\(i\) 右侧第一个 1 的位置(如没有则为 \(n+1\)),也就是可能挡住 \(s_i\) 右移的位置。\(wt_i\)\(s_i\) 总共空等的轮数。则显然 \(\max_{s_i=1} \{c_i+wt_i\}\)
这样,关键就变成求 \(wt[i]\),这里分几种情形

\(1^o\) \(nxt_i=i+1\)\(wt_{nxt_i}=0\)
\(i\)等一轮后就不会再空等,\(wt_i=1\)

\(2^o\) \(nxt_i=i+1\)\(wt_{nxt_i}>0\)
一旦 \(nxt_i\) 等一轮,\(i\) 随之也会(在下一轮)等一轮,故 \(wt_i=wt_{nxt_i}+1\)

\(3^o\) \(nxt_i=i+1\)
则之间有若干个 0,每个 0 可以帮 \(i\) 至多节省一轮时间,当然 \(wt_i\) 也不可能为负,故 \(wt_i=\max(0,wt_{nxt_i}+1-(nxt_i-i-1))\)

\(4^o\) \(c_i=0\)
这时候 \(i\) 根本不用移,则直接 \(wt_i=0\)

正解代码:

#include<bits/stdc++.h>
using namespace std;
string s;
long long n,wt[300005],nxt[300005],ans;
int main() 
{
    cin>>s;
    n=s.length();
    for(long long i=n,k=n+1,cnt=0;i>=1;i--)
    {
        if(s[i-1]=='0') cnt++;
        else
        {
            nxt[i]=k; 
            k=i;
            wt[i]=max((long long)0,wt[nxt[i]]+1-(nxt[i]-i-1));
            if(cnt) ans=max(ans,cnt+wt[i]);
        }
    }
    cout<<ans;
    return 0;
}
posted @ 2024-06-19 11:28  xlf2011  阅读(8)  评论(0编辑  收藏  举报