【Codeforces】constructive marathon(div. 2)C. Equal Binary Subsequences | 构造题单

169 阅读2分钟

感谢 Misono_Mika 大佬创建的构造题单qwq!

题目链接

Problem - C - Codeforces

题目

image.png

题目大意

给定一个长度为 2n2n01 串,要求执行恰好一次下述操作:

  • 选择s的任何子序列(可能为空)并将其向右旋转一个位置。即选择索引序列 1b1<b2<<bm2n1≤b_1<b_2<…<b_m≤2n。之后令
sb1=sbmsb2=sb1sbm=sbm1s_{b_1}=s_{b_m}\\ s_{b_2}=s_{b_1}\\ …\\ s_{b_m}=s_{b_{m−1}}。

在执行一次操作后,判断能否将 ss 划分为两个不相交的相等子序列。

ss 划分为两个不相交的相等子序列 spspsqsq 相当于:
构造 p1<p2<<pnp_1<p_2<…<p_nq1<q2<<qnq_1<q_2<…<q_n 的两个数组,使得从 112n2n 的每个整数在 ppqq 中恰好出现一次,且

sp=sp1sp2spn,sq=sq1sq2sqn,sp=sqsp=s_{p_1}s_{p_2}…s_{p_n},\\ sq=s_{q_1}s_{q_2}…s_{q_n},\\ sp=sq

在执行一次操作后能将 ss 划分为两个不相交的相等子序列,则输出 bb 中元素和 pp 中元素。无解输出 -1

思路

操作不会改变数组中 01 的数量,如果整个数组 ss 中有奇数个 1,那么无解。

当数量为偶数时,我们把 2n2n 个数划分成 nn 组,其中第 i(1in)i(1\le i\le n) 组为 (s2i1,s2i)(s_{2i-1},s_{2i})。令 s2i1s2is_{2i-1}\neq s_{2i} 的段的数量为 xx,显然 xx 是偶数。

遍历这 xx 个两元素不相等的组,我们顺次编号为 [1,x][1,x]。在每组中按照以下规则选出一个元素:若编号为奇数,选择值为 11 的下标;若编号为偶数,选择值为 00 的下标。

这样我们进行一次右旋后,每个组中两个元素均相同。那么我们的 pp 即为 1,3,5,...,2n11,3,5,...,2n-1

代码

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <math.h>
#include <map>
#include <queue>
#include <vector>
#include <stdlib.h>
#include <time.h>
using namespace std;
using LL=long long;
const int N=200001;
int n,m,k,a[N];
int ans[N];
LL solve()
{
	int tot=0;
	scanf("%d",&n);
	for (int i=1;i<=n*2;++i) scanf("%1d",&a[i]),tot+=a[i];
	if (tot&1) return printf("-1\n"),0;
	tot=0;
	for (int i=1;i<=n;++i)
	{
		if (a[i*2-1]==a[i*2]) continue;
		++tot;
		if (tot&1)
			if (a[i*2-1]) ans[tot]=i*2-1;
			else ans[tot]=i*2;
		else
			if (a[i*2-1]) ans[tot]=i*2;
			else ans[tot]=i*2-1;
	}
	printf("%d",tot);
	for (int i=1;i<=tot;++i) printf(" %d",ans[i]);
	printf("\n");
	for (int i=1;i<=n;++i) printf("%d ",i*2-1);
	printf("\n");
	return 0;
}
int main()
{
	int T=1;
	scanf("%d",&T);
	while (T--) solve();
	return 0;
}