蒟蒻君的刷题日记Day17:CF809D 题解

思路

Step1: 直接 DP

  • 状态定义

f [ i ] [ j ] f[i][j] f[i][j] a [ 1 ] → a [ i ] a[1] \rightarrow a[i] a[1]a[i] 中取长度为 j j j 的严格上升子序列时最后一个数的最小值。

  • 状态转移

分三种情况讨论:

  1. f [ i − 1 ] [ j − 1 ] < l f[i-1][j-1]<l f[i1][j1]<l f [ i ] [ j ] = l f[i][j]=l f[i][j]=l
  2. f [ i − 1 ] [ j − 1 ] ≥ r f[i-1][j-1] \ge r f[i1][j1]r:状态不合法,不用转移了;
  3. l ≤ f [ i − 1 ] [ j − 1 ] < r l \le f[i-1][j-1] < r lf[i1][j1]<r f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + 1 f[i][j]=f[i-1][j-1]+1 f[i][j]=f[i1][j1]+1

Step2: Splay 优化

  • 对于第一种, f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1] 没用,直接赋值即可;
  • 对于第三种,比较麻烦,可以先把最靠 r r r f f f 删除,再插入一个 l l l,就可以移动整个区间。
  • 题目思维很巧妙,但代码比较难实现(

代码

常数比较大,卡了挺长时间常才 AC 的(

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
struct node {
    int s[2], fa, v, tag;
} t[N];
#define ls t[id].s[0]
#define rs t[id].s[1]
inline void rotate(int id) {
	int fa = t[id].fa, ffa = t[fa].fa;
	node &pre = t[id], &f1 = t[fa], &f2 = t[ffa];
	bool c = f1.s[1] == id, cc = c ^ 1, c1 = f2.s[1] == fa;
	if (ffa) {
		f2.s[c1] = id;
	}
	int &son = pre.s[cc];
	pre.fa = ffa, f1.s[c] = son;
	if (son) {
		t[son].fa = fa;
	}
	son = fa, f1.fa = id;
}
inline void pushdown(int id) {
	if (!t[id].tag) {
        return ;
    }
    int &tt = t[id].tag;
	if (ls) {
        t[ls].v += tt, t[ls].tag += tt;
    }
	if (rs) {
        t[rs].v += tt, t[rs].tag += tt;
    }
    tt = 0;
}
int root;
void splay(int id, int f) {
	stack<int> S;
	S.push(id);
	for (int i = id; t[i].fa; i = t[i].fa) {
		S.push(t[i].fa);
	}
	while (S.size()) {
		pushdown(S.top()), S.pop();
	}
	while (t[id].fa != f) {
		int fa = t[id].fa, ffa = t[fa].fa;
		if (ffa != f) {
			rotate((t[fa].s[0] == id) == (t[ffa].s[0] == fa) ? fa : id);
		}
		rotate(id);
	}
	if (!f) {
		root = id;
	}
}
int find(int x) {
	int id = root, res = 0;
	while (id) {
		pushdown(id);
		if (t[id].v < x) {
			res = id, id = rs;
		} else {
			id = ls;
		}
	}
	return res;
}
inline int Nxt(int id) {
    splay(id, 0), id = rs;
    while (ls) {
        id = ls;
    }
    return id;
}
inline int Pre(int id) {
    splay(id, 0), id = ls;
    while (rs) {
        id = rs;
    }
    return id;
}
inline void del(int id) {
	int pre = Pre(id), nxt = Nxt(id);
	splay(pre, 0), splay(nxt, pre);
	t[nxt].s[0] = t[id].fa = 0;
}
int k;
void ins(int x) {
	int id = root, fa = 0;
	while (id) {
		pushdown(id);
		fa = id, id = t[id].v > x ? ls : rs;
	}
	id = ++k;
	if (fa) {
		bool tt = t[fa].v <= x;
        t[fa].s[tt] = id;
    }
	t[id].fa = fa, t[id].v = x;
    splay(id, 0);
}
int main() {
	ios :: sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
    ins(-1e9 - 10), ins(1e9 + 10);
    int n;
    cin >> n;
	int res = n;
    while (n--) {
    	int l, r;
    	cin >> l >> r;
        int u = find(l), v = find(r), nxt = Nxt(v);
        if (u ^ v) {
            splay(u, 0), splay(nxt, u);
            node &nxtt = t[t[nxt].s[0]];
            ++nxtt.v, ++nxtt.tag;
        }
        if (nxt != 2) {
            del(nxt), --res;
        }
        ins(l);
    }
    cout << res << '\n';
	return 0;
}
  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻一枚

谢谢鸭~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值