题面
大意就是:给两个\(2n\)数组\(A,B\),要生成一个单调不降序列\(C\),使得\(C_i=A_i\)或\(C_i=B_i\)。并且有恰好\(n\)个位置选择了\(C_i=A_i\)。任意一种方案。
题解
现场得分:100/100
- 记\(A\)数组为第0个数组,\(B\)数组为第1个数组。
- 记dp:\(f_{i,0/1,0/1}\),表示从\(1\)到\(i\),第\(i\)个当中用的是第0/1个数组,尽可能地多用0/1,最多能用多少个。
- 这个很好转移
- 然后有个结论:如果\(n\leq min(f_{2n,0,0},f_{2n,0,1})\)或者\(n\leq min(f_{2n,1,0},f_{2n,1,1})\)就一定有解。感性地理解,就是你尽可能选0,可以超过一半;尽可能选1,也可以超过一半。
- 为什么?发现其中关键是能否在这个框出的区间里每个值都取到。(最多\(x\)个,最少\(y\)个,那么\([y,x]\)每个值都能取到)。
- 考虑相邻两个的关系。
- 先简化一下,我们记\(min_i=min(a_i,b_i),max_i=max(a_i,b_i)\)把第\(i\)个位置上的看成一个\(p_i=[min_i,max_i]\)的区间,发现这是不影响的。
- 如果\(p_i\)与\(p_{i+1}\)相离,显然不影响。
- 如果\(p_i\)与\(p_{i+1}\)有包含关系,显然就没有自由选择余地了,不影响。
- 如果\(p_i\)与\(p_{i+1}\)交叉,但是\(min_{i+1}<min_{i}\),显然就没有自由选择余地了,不影响。
- 剩下一种情况:\(min_i\leq min_{i+1}\leq max_i\leq max_{i+1}\)。你会发现这是两个递增序列,你随时可以从底下一个跳到上面一个,因此也是满足的。
- 我们就证完了
- 然后考虑怎么构造
- 我们倒着跑一遍,每时每刻都保持尽量选0和尽量选1的最大个数始终大于等于\(n\)。
代码
#include<bits/stdc++.h>
#define LL long long
#define MAXN 500100
using namespace std;
template<typename T>void Read(T &cn)
{
char c;int sig = 1;
while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
if(cn < 0) {putchar('-'); cn = 0-cn; }
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
while(cn)cm = cm*10+cn%10,cn/=10,wei++;
while(wei--)putchar(cm%10+48),cm/=10;
putchar(cx+48);
}
int n;
int a[MAXN*2+1], b[MAXN*2+1];
int ans[MAXN*2+1];
int f[MAXN*2+1][2][2];
void getit(int a[], int n) {for(int i = 1;i<=n;i++) Read(a[i]); }
void geng(int cn, int cm, int cx, int cy) {if(cy > f[cm][cx][cn]) f[cm][cx][cn] = cy; }
int nong()
{
for(int i = 0;i<=1;i++) for(int j = 0;j<=1;j++) f[1][i][j] = (i==j);
for(int i = 2;i<=n*2;i++)
{
for(int j = 0;j<=1;j++) for(int k = 0;k<=1;k++) f[i][j][k] = -n*2;
if(a[i] >= a[i-1]) geng(0,i,0,f[i-1][0][0]+1), geng(1,i,0,f[i-1][0][1]);
if(a[i] >= b[i-1]) geng(0,i,0,f[i-1][1][0]+1), geng(1,i,0,f[i-1][1][1]);
if(b[i] >= a[i-1]) geng(0,i,1,f[i-1][0][0]), geng(1,i,1,f[i-1][0][1]+1);
if(b[i] >= b[i-1]) geng(0,i,1,f[i-1][1][0]), geng(1,i,1,f[i-1][1][1]+1);
}
int lei1 = 0, lei2 = 0, lst = max(a[2*n],b[2*n])+1;
for(int i = n*2;i>=1;i--)
{
if(lei1 + f[i][0][0] >= n && lei2 + f[i][0][1] >= n && a[i] <= lst) {lei1++; ans[i] = 0; lst = a[i]; continue; }
if(lei1 + f[i][1][0] >= n && lei2 + f[i][1][1] >= n && b[i] <= lst) {lei2++; ans[i] = 1; lst = b[i]; continue; }
return 0;
}
return 1;
}
int main()
{
Read(n);
getit(a,n*2); getit(b,n*2);
if(!nong()) {puts("-1"); return 0; }
for(int i = 1;i<=n*2;i++) putchar('A'+ans[i]); puts("");
return 0;
}