C - 行列のできるドーナツ屋

[Q] https://atcoder.jp/contests/donuts-2015/tasks/donuts_2015_3
単調増加列の最長一致(LIS)をこねます。

入力例を考える

6
5 4 3 6 2 1

5の人からみたら 0人
4の人から見たら {5} で1人
3の人から見たら {5,4} で2人
6の人から見たら {5,4,3} で3人
2の人から見たら {6} で1人
1の人から見たら {6,2} で2人

目線の人を除いた「単調減少列」の大きさが答えになっていそう。だけど、目線の人ごとに探索するとO(N^2)になってしまう。

使えそうなアルゴリズムに、
LIS 単調増加列の最長一致
https://atcoder.jp/contests/typical90/tasks/typical90_bh
が思い当たります。

テンプレをいくらか改良して、
1, 増加列を減少列にしたいので、A[i]をマイナスにする
2, 最長一致ではないので、更新点(cnt)を常に更新する
ことをします。

   DP[ ] = -A   ans  DP
              // 0
   DP[0] = -5 // 1   {-5}
   DP[1] = -4 // 2   {-5, -4}
   DP[2] = -3 // 3   {-5, -4, -3}
   DP[0] = -6 // 1   {-6}
   DP[1] = -2 // 2   {-6, -2}
   DP[2] = -1        {-6, -2, -1}


LISの典型を解けばおのずと問題も解けるので、有能解説に飛ぶのがいいかもしれない。

https://atcoder.jp/contests/donuts-2015/submissions/25521483

#include <bits/stdc++.h>
using namespace std;
using ll=long long;

#define rep(i,n) for (ll i=0; i<n; ++i)

// LIS 単調増加列の最長一致
/*
https://atcoder.jp/contests/typical90/tasks/typical90_bh
*/
ll DP[100010];
ll A[100010];
int main(){
 cin.tie(0);
 ios::sync_with_stdio(0);
 cout << fixed << setprecision(10);
 ll N;
 cin >> N;
 rep(i, N){
   cin >> A[i];
   A[i] *= -1;
 }
 ll cnt = 0;
 rep(i, N){
   cout << cnt << endl;
   ll pos = lower_bound(DP, DP+cnt, A[i]) - DP;
   DP[pos] = A[i];
   cnt = pos+1;
 }
}

この記事が気に入ったらサポートをしてみませんか?