1. 程式人生 > 實用技巧 >LOJ#3271. 「JOISC 2020 Day1」建築裝飾 4 DP+找規律

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