感谢联考集团

感觉挺有意思的一个题。

考虑一个直接贪心的做法。

首先发现连续三段长度必然 \(\geq i+1\),所以 \(\sum ans\)\(O(n \ln n)\) 级别的。

考虑一下我们能不能直接找出这些段。

那么首先枚举 \(i\),然后按照以下方式贪心。

记上一次选的段是 \([x,las]\)\(o\) 的下一个 \(1\)\(\text{next}(o)\)

我们贪心的去想肯定希望 \([las+1,y]\)\(y\) 最大,找到这么一个 \(y\) 容易 \(O(1)\) 实现。

考虑这样会出现的问题,如果 \([las+1,las+i]\) 里面全是 \(0\),那么我们需要进行一个调整,使得左边的一部分 \(0\) 被分到上一段。

我们可以对于前面的段都记下来,首先一个段如果只有一个 \(1\),那么我们做不到对这个段进行调整。

考虑上一个有 \(\geq 3\)\(1\) 的段,分裂出最后两个 \(1\),接下来讨论一点细节。

现在序列长这样:\(\dots 1 \dots 1\:s_1\:s_2\:s_3\dots 0,0,0,0,0,0,0,0\)

其中 \(s_i\) 是一个只有一个 \(1\) 的段,记第一个 \(1\) 的位置是 \(p\),第二个是 \(q\),那么考虑以下事情。

我们必然会在 \(p-1\) 之前进行一次分裂,不然相当于没有调整。

考虑如果我们将 \(p,q,\text{next}(q)\) 分成一段(不妨假设它们可以),那么 \(\text{next}(q)\) 后面会空出来若干个 \(0\),如果没有那么相当于没有调整,那这样就寄了,甚至不如原来的。

那么也就是说肯定要分成 \([p,q-1]\) 的。

而且这样还使 \(q\) 在它们段的第一个,能取掉后面尽可能多的 \(0\)

如果 \(q,\text{next}(q),\text{next(next(}q\text{))}\) 能放在一段的话,那么你就出事了,说明 \(\text{next(next(}q\text{))}\) 是在它们段的第一个,那么调整其实没用。

然后你发现,我们似乎直接令 \(las \to q-1\) 就行了,然后更新一下答案。

分析一下时间复杂度:

如果这次调整成功了,接下来又遇到一个全 \(0\) 段,那么 \(q' >q\) 是必然的,如果不存在 \(q'>q\) 的那么这次调整必然失败,所以我们可以 \(las \to q-1\)\(q \to 0\)

然后时刻维护 \(las,q,ans\) 即可。

最坏情况你的 \(las\) 指针会扫过每一个位置两次,所以复杂度还是 \(O(n \ln n)\)

放个代码可能好理解一些:

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 2e6+5;

char s[MAXN];

int n, pos[2], res[MAXN];
int pre[MAXN], l[MAXN][2], L[MAXN];

inline int calc(int x){
	int now=0, r, o, ans=0, pos, las=-1, lasans;
	while(now<n){
		r=min(n,now+x);
		o=l[r][(pre[now]&1)^1];
		if(o==-1 || o<=now){
			if(las==-1) return -1;
			pos=L[las+1];
			ans=lasans+1; now=pos-1; las=-1;
			continue;
		}
		if(pre[o]-pre[now]>=2) las=o, lasans=ans+1;
		now=o; ans++;
	}
	return ans;
}

inline void solve(){
	scanf("%s",s+1); n=strlen(s+1);
	for(int i=1;i<=n;++i) pre[i]=pre[i-1]+(s[i]=='1');
	pos[0]=0; pos[1]=-1; int p=0;
	for(int i=1;i<=n;++i) L[i]=p, pos[pre[i]&1]=i, l[i][0]=pos[0], l[i][1]=pos[1], p=s[i]=='1'?i:p;
	for(int i=1;i<=n;++i) printf("%d ",calc(i)); putchar(10);
}

int main(){
	//freopen("partition.in","r",stdin);
	//freopen("partition.out","w",stdout);
	int t; scanf("%d",&t);
	while(t--) solve();
	return 0;
}
posted @ 2024-02-08 19:41  OccDreamer  阅读(15)  评论(0)    收藏  举报