Longest Increasing Subsequence

考虑类似 LIS 的 DP 做法,设 fif_i 为以 ii 为结尾,填了 1-1 进去后,能达成的最长上升子序列。

考虑 fi=max1j<iaj<aifj+pj,i+1f_i = \max \limits_{1 \leq j < i \land a_j <a_i} f_j + p_{j,i} + 1

其中 pj,ip_{j,i} 指的是子序列选左端为 jj,右端为 ii,中间将 1-1 以最优的方式放的答案。

首先 pp 必不超过 jij \sim i1-1 的个数。但是这个上界并不一定能达到。

此外,jij \sim i 中填的数应该在 (aj,ai)(a_j,a_i) 范围内。于是我们可以维护前缀和。设 sis_i 表示前 ii 个中 1-1 的数的个数,gig_i 为能选择填 1-1 的数中 i\leq i 的个数。

那么 pj,i=min{sisj1,gai1gaj}p_{j,i} = \min\{s_i-s_{j-1},g_{a_i-1} - g_{a_j}\}

那么 DP 转移为 fi=max1j<iaj<aifj+min{sisj1,gai1gaj}+1f_i = \max \limits_{1 \leq j < i \land a_j <a_i} f_j + \min\{s_i-s_{j-1},g_{a_i-1} - g_{a_j}\} + 1

min\min 拆开,发现变成了一个三维偏序。CDQ 优化 DP 即可。

// LUOGU_RID: 119204826
// LUOGU_RID: 119190526
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <vector>
#include <unordered_map>
using namespace std;

#define	Cobi lower_bound
#define ll long long
#define Dopi dp

const int N = 6e5 + 25;
int n, m, k;

ll a[N], b[N], s[N];
vector<int> v;
int from[N];
int s2[N];

struct Node
{
	ll id, val, s, s2, ns2;

	Node() = default;

	Node(int id, int s, int s2, int ns2, int val)
		: id(id), s(s), s2(s2), ns2(ns2), val(val)
	{
	}
}p[N], tmp[N];

bool cmp1(const Node& a, const Node& b)
{
	return a.id < b.id;
}

bool cmp2(const Node& a, const Node& b)
{
	return a.val < b.val;
}
class Bit
{
public:
	long long tr[N];
	ll lowbit(ll x)
	{
		return x & -x;
	}
	void update(ll x, ll v) // Good luck in your first contest and you will meet zcw
	{
		while (x < N)
		{
			tr[x] = max(tr[x], v);
			x += lowbit(x);
		}
	}
	ll query(int x)
	{
		ll res = -2e10;
		while (x)
		{
			res = max(res, tr[x]);
			x -= lowbit(x);
		}
		return res;
	}
	void CLEAR(ll x)
	{
		while (x < N)
		{
			tr[x] = -2e10;
			x += lowbit(x);
		}
	}
}bt;

long long Dopi[N];
int f[N], na[N], idx, ns[N];
int ra[N];
int ispre[N], lst[N];

int fans[N], rrp[N];
bool isp[N];

// min(s_r - s_{l-1}, s2_{a_r-1}-s2_{al})
// s_r - s_{l-1} <= s2_{a_r-1}-s2_{al} => s2_{al}-s_{l-1} <= s2_{a_r-1}-s_r (-s_{l-1})
// s2_{a_r-1}-s2_{al} <= s_r - s_{l-1} => s_{l-1} - s2_{al} <= s_r - s2_{a_r-1} (-s2_{a_l})

void cdq(int l, int r)
{
	if (l == r) return;
	sort(p + l, p + r + 1, cmp1);
	for (int i = l; i <= r; i++) tmp[i] = p[i];
	int mid = l + r >> 1;
	cdq(l, mid);
	sort(p + l, p + mid + 1, cmp2);
	sort(p + mid + 1, p + r + 1, cmp2);
	int i = l, j = mid + 1;
	unordered_map<int, int> mp, mp2;
	while (j <= r)
	{
		while (i <= mid && p[i].val < p[j].val)
		{
			bt.update(p[i].s2 - p[i].s + 200000, Dopi[p[i].id] - p[i].s);
			if (!mp.count(Dopi[p[i].id] - p[i].s))
			{
				mp[Dopi[p[i].id] - p[i].s] = p[i].id;
				mp2[Dopi[p[i].id] - p[i].s] = p[i].s2 - p[i].s;
			}
			else if (p[i].s2 - p[i].s < mp2[Dopi[p[i].id] - p[i].s])
			{
				mp[Dopi[p[i].id] - p[i].s] = p[i].id;
				mp2[Dopi[p[i].id] - p[i].s] = p[i].s2 - p[i].s;
			}
			i++;
		}
		long long g = bt.query(p[j].ns2 - p[j].s + 200000);
		long long nv = g + 1 + p[j].s;
		if (nv > Dopi[p[j].id])
		{
			Dopi[p[j].id] = nv;
			lst[p[j].id] = mp[g];
		}
		else if (nv == Dopi[p[j].id])
		{
			lst[p[j].id] = min(lst[p[j].id], mp[g]);
		}
		j++;
	}
	for (int g = l; g <= mid; g++)
	{
		bt.CLEAR(p[g].s2 - p[g].s + 200000);
	}
	i = l, j = mid + 1;
	mp.clear();
	mp2.clear();
	while (j <= r)
	{
		while (i <= mid && p[i].val < p[j].val)
		{
			bt.update(p[i].s - p[i].s2 + 200000, Dopi[p[i].id] - p[i].s2);
			if (!mp.count(Dopi[p[i].id] - p[i].s2))
			{
				mp[Dopi[p[i].id] - p[i].s2] = p[i].id;
				mp2[Dopi[p[i].id] - p[i].s2] = p[i].s - p[i].s2;
			}
			else if (p[i].s - p[i].s2 < mp2[Dopi[p[i].id] - p[i].s2])
			{
				mp[Dopi[p[i].id] - p[i].s2] = p[i].id;
				mp2[Dopi[p[i].id] - p[i].s2] = p[i].s - p[i].s2;
			}
			i++;
		}
		long long g = bt.query(p[j].s - p[j].ns2 + 200000);
		long long nv = g + 1 + p[j].ns2;
		if (nv > Dopi[p[j].id])
		{
			Dopi[p[j].id] = nv;
			lst[p[j].id] = mp[g];
		}
		else if (nv == Dopi[p[j].id])
		{
			lst[p[j].id] = min(lst[p[j].id], mp[g]);
		}
		j++;
	}
	for (int g = l; g <= mid; g++)
	{
		bt.CLEAR(p[g].s - p[g].s2 + 200000);
	}
	for (int i = l; i <= r; i++)
	{
		p[i] = tmp[i];
	}
	cdq(mid + 1, r);
}

signed main()
{
	//freopen("D:\\111.txt", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr);
	cin >> n;
	for (int i = 1; i < N; i++) bt.CLEAR(i);
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		v.emplace_back(a[i]);
	}
	cin >> k;
	for (int i = 1; i <= k; i++)
	{
		cin >> b[i];
		v.emplace_back(b[i]);
	}
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
	for (int i = 1; i <= n; i++)
	{
		int p = a[i];
		a[i] = Cobi(v.begin(), v.end(), a[i]) - v.begin() + 1;
		ra[a[i]] = p;
	}
	multiset<int> st;
	for (int i = 1; i <= k; i++)
	{
		int p = b[i];
		b[i] = Cobi(v.begin(), v.end(), b[i]) - v.begin() + 1;
		ra[b[i]] = p;
		st.insert(b[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		s[i] = s[i - 1] + (ra[a[i]] == -1);
	}
	for (int i = 1; i <= k; i++)
	{
		s2[b[i]] = 1;
		//cout << "!!: " << b[i] << "\n";
	}
	for (int i = 1; i < N; i++) s2[i] += s2[i - 1];
	//cout << "s: ";
	//for (int i = 1; i <= n; i++) cout << s[i] << " ";
	//cout << "\ns2: ";
	//for (int i = 1; i <= n + k; i++) cout << s2[i] << " ";
	//cout << "\n";
	for (int i = 1; i <= n; i++)
	{
		if (ra[a[i]] != -1)
		{
			na[++idx] = a[i];
			ns[idx] = s[i];
			//cout << na[idx] << " " << ns[idx] << "\n";
			rrp[idx] = i;
		}
	}
	na[0] = 0, na[++idx] = n + k + 5;
	ns[idx] = s[n];
	rrp[idx] = n + 1;
	rrp[0] = 0;
	Dopi[0] = 1;
	p[0] = Node(0, 0, 0, 0, 0);
	for (int i = 1; i <= idx; i++)
	{
		Dopi[i] = 1;
		lst[i] = i;
		p[i] = Node(i, ns[i], s2[na[i]], s2[na[i] - 1], na[i]);
		//cout << "!!: " << i << " " << ns[i] << " " << s2[na[i]] << "\n";
	}
	/*for (int i = 1; i <= idx; i++)
	{
		Dopi[i] = 1;
		lst[i] = i;
		for (int j = 0; j < i; j++)
		{
			if (na[j] < na[i])
			{
				//cout << i << " " << j << " " << ns[i] << endl;
				if (Dopi[j] + max(0LL, min(ns[i] - ns[j], s2[na[i] - 1] - s2[na[j]])) + 1 >= Dopi[i])
				{
					Dopi[i] = Dopi[j] + max(0LL, min(ns[i] - ns[j], s2[na[i] - 1] - s2[na[j]])) + 1;
					lst[i] = j;
				}
			}
		}
		//cout << i << " " << Dopi[i] << "\n";
	}*/
	//cout << idx << "\n";
	cdq(0, idx);
	vector<int> pos;
	int id = idx;
	pos.emplace_back(id);
	while (lst[id] != id)
	{
		id = lst[id];
		pos.emplace_back(id);
	}
	reverse(pos.begin(), pos.end());
	for (auto& i : pos)
	{
		if (i != 0 && i != idx)
		{
			isp[rrp[i]] = 1;
		}
	}
	//for (auto& i : pos)
	//{
	//	cout << i << " ";
	//}
	//cout << "\n";
	//cout << "\n";
	int lstone = 0;
	int c = 0;
	//cout << (pos.size() >= 3 ? pos[1] : 1) << " " << (pos.size() >= 3 ? pos[pos.size() - 2] : n) << "\n";
	int now = idx;
	while (now)
	{
		int l = rrp[lst[now]], r = rrp[now];
		lstone = a[l];
		for (int j = l; j <= r; j++)
		{
			if (ra[a[j]] != -1) continue;
			auto it = st.upper_bound(lstone);
			if (it != st.end())
			{
				fans[j] = ra[*it];
				lstone = *it;
				st.erase(it);
				c++;
			}
		}
		now = lst[now];
	}
	for (int i = 1; i <= n; i++)
	{
		if (ra[a[i]] != -1)
		{
			fans[i] = ra[a[i]];
		}
		else if (!fans[i])
		{
			fans[i] = ra[*st.begin()];
			st.erase(st.begin());
		}
		cout << fans[i] << " ";
	}
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示