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;
}