★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; }
まとめ
解法を理解してしまえばシンプル。