CF1658F Juju and Binary String 做题记录

对于一个 01 序列 bb,定义 f(b)=[bi=1]bf(b)=\frac{\sum\limits [b_i=1]}{|b|},即 bb11 的个数除以它的长度。

现给定一个长度为 nn 的序列 aa 和一个正整数 mm,请你找到 aa 的若干不相交子串使得这些它们拼起来后得到的 01 串 pp 满足 p=m|p|=mf(p)=f(a)f(p)=f(a)。输出方案。

1mn2×1051\le m\le n\le 2\times 10^5

c1=[ai=1],x=c1mnc1=\sum[a_i=1],x=\frac{c1\cdot m}{n}pp 中需要的 11 的个数。

首先显然若 c1mn\frac{c1\cdot m}{n} 不是整数即 mm 不是 ngcd(n,c1)\frac{n}{\gcd(n,c1)} 的倍数则无解,否则一定有解。

对于这种题,一般猜想答案 2\le 2。那么猜想最多只需要两段,考虑证明。

考虑最多两段不交的区间可能是什么,不难想到它有可能是一个环上的一段区间。那么考虑把 aa 放到环上,即令 ai+n=aia_{i+n}=a_i。设 ci=j=ii+m1[aj=1]c_i=\sum\limits_{j=i}^{i+m-1}[a_j=1],考察 cc 的性质:

  1. cici+11|c_{i}-c_{i+1}|\le 1:显然,因为滑动窗口不可能同时加入/删除两个 11

  2. yN,min{ci}ymax{ci},ck=y\forall y\in \mathbb{N},\min\{c_i\}\le y\le \max\{c_i\},\exist c_k=y:通过第一条性质不难得出,因为无法“跳过”某个 yy

  3. min{ci}xmax{ci}\min\{c_i\}\le x\le \max\{c_i\}

    考虑反证,假设 min{ci}>x\min\{c_i\}>x,则 i=1nci>c1m\sum\limits_{i=1}^n c_i>c1\cdot m。注意到 i=1nci=c1m\sum\limits_{i=1}^n c_i=c1\cdot m 因为每个 11 都被计算了 mm 次。所以有 c1m>c1mc1\cdot m>c1\cdot m,矛盾。

    max{ci}x\max\{c_i\}\ge x 的证明同理。

根据第二条和第三条性质可以得出 ck=x\exist c_k=x,那么找到这个 kk 即可找到环上符合条件的区间,即序列上最多两个不交的区间。

所以最多只需要两段。

代码如下:

#include <iostream>
#include <cstdio>

using namespace std;

const int S=200005;

int n,m;
char a[S];
int c1,c[S];

inline int gcd(int x,int y)
{
	int t=x%y;
	while(t!=0) x=y,y=t,t=x%y;
	return y;
}

inline void slove()
{
	scanf("%d%d",&n,&m);
	scanf("%s",a+1);
	for(int i=1;i<=n;i++) c[i]=c1=0;
	for(int i=1;i<=n;i++) c1+=a[i]=='1';
	if(m%(n/gcd(n,c1))!=0) return puts("-1"),void();
	int x=1ll*c1*m/n;
	for(int i=1;i<=m;i++) c[1]+=a[i]=='1';
	for(int i=2;i<=n;i++)
	{
		c[i]=c[i-1];
		c[i]-=a[i-1]=='1';
		c[i]+=a[(i+m-1-1)%n+1]=='1';
	}
	for(int i=1;i<=n;i++)
	{
		if(c[i]==x)
		{
			if(i+m-1<=n) printf("1\n%d %d\n",i,i+m-1);
			else printf("2\n%d %d\n%d %d\n",i,n,1,i+m-1-n);
			return;
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T-->0) slove();
	return 0;
}