线段树 ---- Codeforces 737 Div2 D. Ezzat and Grid 维护dp

题目链接


题目大意:

就是给你很多行的 01 01 01串,长度是 1 e 9 1e9 1e9,每一行都有若干段的连续的 1 1 1,对于给定的串集合是 美 丽 的 美丽的 的条件是任意相邻两行的串至少有一个列是同时有 1 1 1的,问你至少删除多少行使得这些 01 01 01是美丽的?
n , m ∈ [ 3 e 5 ] n 是 行 数 , m 是 线 段 数 n,m\in[3e5]n是行数,m是线段数 n,m[3e5]nm线
输入是 i , l , r i,l,r i,l,r意思是第 i i i行的 [ l , r ] [l,r] [l,r]的范围内是 1 1 1


解题思路:

  1. 这个题目有个比较麻烦的地方就是要输出方案
  2. 首先我们很明显的dp的思想就是 d p [ i ] 表 示 选 了 第 i 行 并 且 以 第 i 行 结 尾 能 构 成 最 大 的 dp[i]表示选了第i行并且以第i行结尾能构成最大的 dp[i]ii 01 串 集 合 01串集合 01
  3. d p [ i ] = 1 + m a x { d p [ j ] } ∣ 如 果 第 j 行 和 i 行 有 同 列 为 1 dp[i] = 1+max\{dp[j]\}|如果第j行和i行有同列为1 dp[i]=1+max{dp[j]}ji1
  4. 但是这个非常不好维护,那么 d p 维 数 + 1 dp维数+1 dp+1进行进一步的细分
  5. d p [ i ] [ j ] dp[i][j] dp[i][j]表式前 i i i行并且在第 j j j列有1的最大集合
  6. 很明 d p [ i ] [ j ] = 1 + m a x k ∈ C i { d p [ i ] [ k ] } C i 是 i 行 里 面 有 1 的 列 dp[i][j] =1+max_{k\in C_i} \{dp[i][k]\} Ci是i行里面有1的列 dp[i][j]=1+maxkCi{dp[i][k]}Cii1
  7. 实际上上面的dp方程可以写成
  8. d p [ i ] [ k ] = 1 + m a x { d p [ i − 1 ] [ k ] } ∣ k ∈ C i dp[i][k] =1+max \{dp[i-1][k]\}|_{k\in C_i} dp[i][k]=1+max{dp[i1][k]}kCi
  9. C i 是 i 行 里 面 有 1 的 列 C_i是i行里面有1的列 Cii1
  10. 那么这个dp是一段区间一段区间的进行更新的!!因为1是连续的那么就是线段树啦线段树维护的是当前行可以跟新的每一列的信息,要求区间加,然后维护区间最大值!!
  11. 因为要输出方案,那么我们在线段树里面保存最大值是被哪一行更新的就可以了

AC代码:

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 600010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
struct Segtree {
    PII tr[maxn << 2];
    int lazy[maxn << 2];
    void build(int rt, int l, int r) {
        tr[rt] = {0,-1};//第一位dp。第二维更新的位置
        lazy[rt] = 0;
        if(l == r) return;
        build(Lson);
        build(Rson);
    }
    void pushdown(int rt) {
        if(lazy[rt]) {
            lazy[rt<<1] = lazy[rt<<1|1] = 1;
            tr[rt<<1] = tr[rt<<1|1] = tr[rt];
            lazy[rt] = 0;
        }
    }
    void pushup(int rt) {
        tr[rt] = max(tr[rt<<1],tr[rt<<1|1]);
    }
    PII quray(int rt, int l, int r, int posl, int posr) {
        if(posl <= l && posr >= r) return tr[rt];
        pushdown(rt);
        PII res = {0,-1};
        if(posl <= mid) res = max(quray(Lson,posl,posr),res);
        if(posr > mid) res = max(quray(Rson,posl,posr),res);
        return res;
    }
    void update(int rt, int l, int r, int posl, int posr, PII mx) {
        if(posl <= l && posr >= r) {
            tr[rt] = mx;
            lazy[rt] = 1;
            return;
        }
        pushdown(rt);
        if(posl <= mid) update(Lson,posl,posr,mx);
        if(posr > mid) update(Rson,posl,posr,mx);
        pushup(rt);
    }
}sgt;

int path[maxn];
vector<int> lis;
vector<PII> lin[maxn];
bool vis[maxn];
inline int getid(int x) {
    return lower_bound(lis.begin(),lis.end(),x) - lis.begin() + 1;
}

int main() {
    IOS;
    int n, m;
    read(n,m);
    for(int i = 1; i <= m; ++ i) {
        int idx, l, r;
        read(idx,l,r);
        lin[idx].push_back({l,r});
        lis.push_back(l);
        lis.push_back(r);
    }
    
    sort(lis.begin(),lis.end();//离散化
    lis.erase(unique(lis.begin(),lis.end()),lis.end());
    int len = lis.size();
    sgt.build(1,1,len);

    for(int i = 1; i <= n; ++ i) 
        for(int j = 0; j < lin[i].size(); ++ j) {
            lin[i][j].first = getid(lin[i][j].first);
            lin[i][j].second = getid(lin[i][j].second);
        }
    
    for(int i = 1; i <= n; ++ i) {
        PII mx = {0,-1};
        for(auto it : lin[i]) mx = max(mx,sgt.quray(1,1,len,it.first,it.second));
        path[i] = mx.second;//保持dp更新路径
        mx.first ++;
        mx.second = i;//区间更新
        for(auto it : lin[i]) sgt.update(1,1,len,it.first,it.second,mx);
    }
    PII mx = sgt.quray(1,1,len,1,len);
    int cur = mx.second;
    int siz = n;
    while(cur != -1) {
        vis[cur] = true;
        siz --;
        cur = path[cur];
    }
    
    cout << siz << "\n";
    for(int i = 1; i <= n; ++ i)
      if(!vis[i])
        cout << i << " ";
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值