かっつのメモ帳

主に競プロ 時々日記

Codeforces #661 Div3 F. Yet Another Segments Subset

問題のリンク

問題概要

クエリ毎にN個の区間が与えられる。次の条件を満たす区間の部分集合のサイズの最大値を求めよ。

  • 部分集合内の任意の区間のペアについて、どちらかが片方を完全に包含している。または共有点を持たないかのいずれかが成立する。

制約

 1 \leqq \sum N \leqq 3000

 1 \leqq l,r \leqq 2\times 10^{5}

解法

区間の相対的な位置関係さえ分かれば問題ないのでまず座圧しておく。

基本的な方針としては、区間長の短いものから見ていき、その区間内で最大で何個区間を包含できるか?のDPをすれば良い。

そのDPを計算する時のイメージとしては次の図の通り。

f:id:KKT89:20200806104044p:plain

 DPX\lbrack i \rbrack: 右端がi以下の区間のみを見た時の最大値

として区間を昇順に並び替えて左から見ていくと、これまでのDPの計算結果から線形時間で DPX\lbrack i \rbrackが計算できて、区間Iの右端まで計算した最大値を Prebestとすると  DP\lbrack i \rbrack=Prebest+1と更新可能。

番兵として最大長の区間を入れておくと最適解には必ずその区間は選ばれるので、  DP\lbrack N \rbrack-1 が答えとなり実装が楽になる。

実装

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long int ll;

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int q; cin >> q;
    while(q--){
        int n; cin >> n;
        vector<array<int,2>> v(n);
        vector<int> z;
        for(int i=0;i<n;i++){
            cin >> v[i][0] >> v[i][1];
            v[i][1]++;
            z.push_back(v[i][0]);
            z.push_back(v[i][1]);
        }
        sort(z.begin(), z.end());
        z.erase(unique(z.begin(), z.end()),z.end());
        int m=z.size();
        for(int i=0;i<n;i++){
            for(int j=0;j<2;j++){
                v[i][j]=lower_bound(z.begin(), z.end(),v[i][j])-z.begin();
            }
        }
        // sort them in increasing order by length
        sort(v.begin(), v.end(),[](auto i,auto j){
            return i[1]-i[0]<j[1]-j[0];
        });
        v.push_back({0,m-1});
        vector<int> dp(n+1);
        vector<int> dpx(m);
        for(int i=0;i<=n;i++){
            int netx=0;
            int prebest=0;
            for(int j=0;j<m;j++){
                dpx[j]=0;
            }
            for(int j=0;j<i;j++){
                if(v[i][0]<=v[j][0]&&v[j][1]<=v[i][1]){
                    while(netx<=v[j][0]){
                        prebest=max(prebest,dpx[netx++]);
                    }
                    dpx[v[j][1]]=max(dpx[v[j][1]],prebest+dp[j]);
                }
            }
            while(netx<=v[i][1]){
                prebest=max(prebest,dpx[netx++]);
            }
            dp[i]=prebest+1;
            if(i==n)break;
            // insertion sort
            for(int j=i-1;j>=0&&v[j+1]<v[j];j--){
                swap(v[j+1],v[j]);
                swap(dp[j+1],dp[j]);
            }
        }
        printf("%d\n",dp[n]-1);
    }
}