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

  

posted @ 2020-08-03 11:07  EM-LGH  阅读(174)  评论(0编辑  收藏  举报