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; }