Official

D - LIS 2 Editorial by m_99


\(2 \times 10^5\) 程度の要素の数列 \(X = (x_1,\ldots,x_N)\) に対するLISは以下のように求められます(後の解法に続けるためによくある \(\mathrm{dp}\) 配列等の定義から細部を調整しています)。

  • \(1 \leq i \leq N\) に対し、\(\mathrm{dp}_i\) を長さ \(i\) の単調増加列の末尾の要素の最小値(存在しない場合は \(\mathrm{Inf}\) ) とし、すべて \(\mathrm{Inf}\) で初期化。
  • \(i=1,2,\ldots,N\) の順に、\(\mathrm{dp_j} \geq x_i\) であるような最小の \(j\) を二分探索で求め、\(\mathrm{dp_j}\)\(x_i\) で更新する。
  • 最終的に \(\mathrm{dp_i} \neq \mathrm{Inf}\) となる \(i\) の個数が答え。

これを本問に適用します。サイズ \(10^9\)\(01\) 列であって、\(\mathrm{dp}_i =x \) なる \(i\) が存在するならば \(x\) 番目の値が \(1\)、そうでなければ \(0\) であるようなものを考えます。すると、\((l_i,r_i)\) に対応する処理は、番号が \(l_i\) 以上の \(1\) を番号が小さい方から高々 \(r_i-l_i+1\)\(0\) にし、\(l_i\) 番目から \(r_i\) 番目までを \(1\) にするという操作になります。これは座標圧縮をした上で区間更新・区間加算の遅延セグ木を使うことで全体で \(\mathrm{O}(N \log N)\) でシミュレーション可能です。
あるいは、\(\mathrm{dp}\) 配列上で値が \(5,6,7,\ldots\)\(1\) ずつ増えていく区間をmapで管理する方法でも十分高速に答えを求められます(「区間を管理」等で調べると資料が見つかります)。

実装例(C++)

#include <bits/stdc++.h>
#include <atcoder/lazysegtree>
using namespace atcoder;
using namespace std;

pair<int,int> op(pair<int,int> a,pair<int,int> b){
	a.first += b.first;
	a.second += b.second;
	return a;
}

pair<int,int> e(){
	return make_pair(0,0);
}

pair<int,int> mapping(int a,pair<int,int> b){
	if(a==-1)return b;
	b.first = b.second * a;
	return b;
}

int composition(int a,int b){
	if(a==-1)return b;
	return a;
}

int id(){
	return -1;
}

int X;
bool f(pair<int,int> x){
	return x.first<=X;
}

int main() {
	
	int N;
	cin>>N;
	
	vector<int> l(N),r(N);
	vector<int> t;
	for(int i=0;i<N;i++){
		cin>>l[i]>>r[i];
		r[i]++;
		t.push_back(l[i]);
		t.push_back(r[i]);
	}
	
	sort(t.begin(),t.end());
	t.erase(unique(t.begin(),t.end()),t.end());
	
	vector<pair<int,int>> temp;
	for(int i=0;i<t.size()-1;i++){
		temp.emplace_back(0,t[i+1]-t[i]);
	}
    
	lazy_segtree<pair<int,int>,op,e,int,mapping,composition,id> seg(temp);
	
	for(int i=0;i<N;i++){
		int il = distance(t.begin(),lower_bound(t.begin(),t.end(),l[i]));
		int ir = distance(t.begin(),lower_bound(t.begin(),t.end(),r[i]));
		X = r[i]-l[i];
		
		int rr = seg.max_right<f>(il);
		X -= seg.prod(il,rr).first;
		seg.apply(il,rr,0);
		
		if(rr!=temp.size()){
			auto g = seg.get(rr);
			g.first -= X;
			seg.set(rr,g);
		}
		
		seg.apply(il,ir,1);
	}
	
	cout<<seg.all_prod().first<<endl;
	
    return 0;
}

posted:
last update: