感谢 Misono_Mika 大佬创建的构造题单qwq!
题目链接
题目
题目大意
给定一个长度为 的 01
串,要求执行恰好一次下述操作:
- 选择s的任何子序列(可能为空)并将其向右旋转一个位置。即选择索引序列 。之后令
在执行一次操作后,判断能否将 划分为两个不相交的相等子序列。
将 划分为两个不相交的相等子序列 和 相当于:
构造 和 的两个数组,使得从 到 的每个整数在 或 中恰好出现一次,且
在执行一次操作后能将 划分为两个不相交的相等子序列,则输出 中元素和 中元素。无解输出 -1
。
思路
操作不会改变数组中 01
的数量,如果整个数组 中有奇数个 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;
}