LOJ#3271. 「JOISC 2020 Day1」建筑装饰 4 DP+找规律
有一个非常显然的 DP:
$f_{i,j,0/1}$ 表示当前 $DP$ 到 $i$,选了 $j$ 个 A,当前位置选的是 A/B 是否可行.
状态数为 $O(n^2)$,转移为 $O(1)$,时间复杂度为 $O(n^2)$.
这个时候就要动用人类智慧:打表.
打表后发现当 $i,0/1$ 固定的时候 $j$ 的合法状态是一个连续段.
所以对于 $f_{i,0/1}$ 只需维护做右端点即可,转移的话取一个区间并集.
输出方案倒着做就行了.
code:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500009
#define ll long long
#define inf 1000000002
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n;
char ans[N<<1];
struct data {
int l,r;
data(int l=0,int r=0):l(l),r(r){}
}f[N<<1][2];
int a[N<<1],b[N<<1];
void upd(data &a,data b) {
a.l=min(a.l,b.l);
a.r=max(a.r,b.r);
}
int main() {
// setIO("input");
scanf("%d",&n);
int x,y,z,m=n<<1;
f[0][1]=data(0,0);
for(int i=1;i<=m;++i) {
scanf("%d",&a[i]);
}
for(int i=1;i<=m;++i) {
scanf("%d",&b[i]);
}
for(int i=1;i<=m;++i) {
f[i][0]=f[i][1]=data(inf,-inf);
if(a[i-1]<=a[i]) upd(f[i][0],f[i-1][0]);
if(b[i-1]<=a[i]) upd(f[i][0],f[i-1][1]);
if(a[i-1]<=b[i]) upd(f[i][1],f[i-1][0]);
if(b[i-1]<=b[i]) upd(f[i][1],f[i-1][1]);
++f[i][0].l,++f[i][0].r;
}
if(n<min(f[m][0].l,f[m][1].l)||n>max(f[m][0].r,f[m][1].r)) {
printf("-1\n");
}
else {
int fl=(n>=f[m][0].l&&n<=f[m][0].r)?0:1;
for(int i=m;i>=1;--i) {
int v=fl?b[i]:a[i];
ans[i]=fl?'B':'A';
n-=(!fl);
if(v>=a[i-1]&&(n>=f[i-1][0].l&&n<=f[i-1][0].r)) fl=0;
else fl=1;
}
for(int i=1;i<=m;++i) {
printf("%c",ans[i]);
}
}
return 0;
}

浙公网安备 33010602011771号