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

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章