kmjp's blog

競技プログラミング参加記です

yukicoder : No.686 Uncertain LIS

★4~4.5位でもいい気はする。
https://yukicoder.me/problems/no/686

問題

N要素の整数列Aを考える。
各要素の上限下限が決まっているとき、AにおけるLISの最長長を求めよ。

解法

通常LISを考えるときは以下を考え、1要素処理する毎に以下の配列を1要素ずつ書き換える。
dp(n) := 数列のprefixをいくつか処理した段階で構成されるn要素の増加列のうち、最終要素の最小値

今回の場合、数列dpにおいて区間[L[i],R[i]-1]に含まれる要素がdp[a..b]とする。
この時、

  • dp[a] := L[i]
  • dp[j+1] := dp[j]+1 (a≦j≦b)

となるようにdpの区間が更新される。

この処理を再現すればよい。
数列の範囲に1追加したり要素を1ずらしたりするので、dpを平方分割して個々のバケットをdequeで持ち、かつ区間全体に対するインクリメント回数を保持すればO(N^1.5)で処理できる。

int N;
const int DI=330;
deque<int> D[DI];
int dif[DI];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	FOR(i,DI) {
		FOR(j,DI) {
			if(i==0 && j==0) D[i].push_back(0);
			else D[i].push_back(1<<20);
		}
	}
	
	cin>>N;
	while(N--) {
		int L,R;
		cin>>L>>R;
		int LS,RS;
		FOR(LS,DI) if(D[LS].back()+dif[LS]>=L) break;
		FOR(RS,DI) if(D[RS].back()+dif[RS]>=R) break;
		if(LS==RS) {
			for(x=DI-1;x>=0;x--) {
				if(x&&D[LS][x-1]+dif[LS]<=R-1) D[LS][x]=min(D[LS][x],max(L-dif[LS],D[LS][x-1]+1));
				if(x==0 && LS && D[LS-1].back()+dif[LS-1]<=R-1) D[LS][x]=min(D[LS][x],max(L-dif[LS],D[LS-1].back()+dif[LS-1]+1-dif[LS]));
			}
			
		}
		else {
			for(x=DI-1;x>=0;x--) {
				if(x&&D[RS][x-1]+dif[RS]<=R-1) D[RS][x]=min(D[RS][x],max(L-dif[RS],D[RS][x-1]+1));
				if(x==0 && RS && D[RS-1].back()+dif[RS-1]<=R-1) D[RS][x]=min(D[RS][x],max(L-dif[RS],D[RS-1].back()+dif[RS-1]+1-dif[RS]));
			}
			for(y=RS-1;y>LS;y--) {
				dif[y]++;
				D[y].pop_back();
				D[y].push_front(D[y-1].back()+dif[y-1]+1-dif[y]);
			}
			for(x=DI-1;x>=0;x--) {
				if(x&&D[LS][x-1]+dif[LS]<=R-1) D[LS][x]=min(D[LS][x],max(L-dif[LS],D[LS][x-1]+1));
				if(x==0 && LS && D[LS-1].back()+dif[LS-1]<=R-1) D[LS][x]=min(D[LS][x],max(L-dif[LS],D[LS-1].back()+dif[LS-1]+1-dif[LS]));
			}
		}
	}
	
	
	int ma=0;
	FOR(i,DI) FOR(j,DI) if(D[i][j]<1<<20) ma=max(ma,i*DI+j);
	cout<<ma<<endl;
}

まとめ

解法を理解してしまえばシンプル。