首页 > 其他分享 >P9870 [NOIP2023] 双序列拓展 题解

P9870 [NOIP2023] 双序列拓展 题解

时间:2024-11-15 20:07:14浏览次数:1  
标签:return 匹配 int 题解 拓展 P9870 序列 NOIP2023 dp

P9870 [NOIP2023] 双序列拓展 题解

NOIP2023 T3,特殊性质题。

什么是特殊性质题?就是题目给出了你极其神秘的性质,从而引导你想出正解。

本篇题解将从部分分的角度,一步步讲述部分分与正解的关系。这样看的话,本题会变得十分简单。

\(\text{Task } 1∼7, O(Tnm)\)

简化题意其实就是要求满足最终序列里的 \((f_i-g_i)\) 同号。那么我们只考虑 \(f_i<g_i\) 的情形,另一种是等价的。

看上去 \(l_0\) 很大,但其实只需要计算出 \(X,Y\) 的 \(L\) 序列一一匹配即可。记匹配序列分别为 \(x,y\)。

那么对于 \(O(nm)\) 的 dp,套路地设 \(dp_{i,j}\) 表示 \(x\) 匹配到 \(i\),\(y\) 匹配到 \(j\) 的合法性。从 \((i,j)\) 转移到 \((i,j+1)\),\((i+1,j)\) 是容易的。

代码:

int DP() {
	if (x[1] == y[1]) return 0;
	int fg = 0;
	if (y[1] > x[1]) fg = 1;
	memset(dp, 0, sizeof dp);
	dp[1][1] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (dp[i][j]) {
				if (fg == 0 && x[i + 1] > y[j]) dp[i + 1][j] = 1;
				if (fg == 1 && y[j] > x[i + 1]) dp[i + 1][j] = 1;
				if (fg == 0 && x[i] > y[j + 1]) dp[i][j + 1] = 1;
				if (fg == 1 && y[j + 1] > x[i]) dp[i][j + 1] = 1;
			}
	return dp[n][m];
}

\(\text{Task } 8∼14, O(T(n+m))\) 特殊性质

dp 的状态是难以优化的,考虑特殊性质是什么意思。

\(x\) 的最小值在序列的最后端,说明这个做法大概和维护一些最小值相关联。又因为这是一个线性的复杂度,使我们想到贪心处理。这样的通常套路是枚举一个变量,同时双指针统计第二个变量去做。

simple 的想法是枚举 \(1\sim m\) 的 \(y\),对于每一个 \(y\) 拓展 \(x\),直到不能拓展为止。并检查合法性。

但这样显然没有正确性,原因是当前 \(y\) “占了过多” 的 \(x\),导致没有足够的合法 \(x\) 和之后的 \(y\) 匹配。

如何解决这个问题?要让更多的 \(y\) 得到匹配,就是不让当前的 \(y\) 去挤占可以去做贡献的更小的 \(x\),也就是 让每个最小的 \(x\) 去拓展更多的 \(y\)。 我们转而枚举 \(x\),对于每个 前缀最小值 \(x_i\),我们可以让它尽情放飞地去匹配尽量多的 \(y\),假设匹配到了 \(y_i\),下一个前缀最小值是 \(x_j\),那么 \([x_i,x_{j-1}]\) 直接匹配 \([y_1,y_i]\) 中的最大值 \(y_{\max}\) 是最优的,至于 \(y_{\max}\) 之后的部分,由于是 \(x_i\) 拓展到的,还有 \(x_j\) 等着匹配这些个拓展出的部分,且让 \(x_j\) 去拓展新的序列也一定更优。这样一来事实上形成了一些个区间。由于 \(y\) 的最大值同样在序列末尾,并不存在 \(x\) 到终点之后没有 \(y\) 与之匹配的情形。那么对于不是前缀最小值的 \(x\),检查它和 \(y_{\max}\) 的大小关系即可。这样做正确的核心是在恰当的时机让当前匹配的 \(y\) "放手",避免 "侵占资源",让那个 \(x\) 来拓展更多的 \(y\)。

代码:

int chk() {
	for (int i = 1; i <= n; i++)
		mx[i] = max(mx[i - 1], y[i]);
	int j = 0, mn = 1e9;
	for (int i = 1; i <= n; i++) {
		if (x[i] < mn) {
			while (j < m && x[i] < y[j + 1])
				++j;
		}
		else if(x[i] >= mx[j]) return 0;
		mn = min(mn, x[i]);
	}
	return j == m;
}

\(\text{Task } 15\sim 20, O(T(n+m))\) 正解

特殊性质的提示还 TM 不够明显吗??

将 \(x,y\) 序列分别按照最大,最小值分成两段处理即可。

完整代码:

#include <bits/stdc++.h>
#define N 500005
#define int long long
using namespace std;
int c, n, m, q;
int x[N], y[N], xx[N], yy[N];
int mx[N];
int nx[N], ny[N];
int chk(int p, int q) {
	if (nx[1] >= ny[1]) return 0;
	mx[0] = -1e9;
	for (int i = 1; i <= q; i++)
		mx[i] = max(mx[i - 1], ny[i]);
	int j = 0, mn = 1e9;
	for (int i = 1; i <= p; i++) {
		if (nx[i] < mn) {
			while (j < q && nx[i] < ny[j + 1])
				++j;
		}
		else if(nx[i] >= mx[j]) return 0;
		mn = min(mn, nx[i]);
	}
	return j == q;
}
int kudo() {
	if (x[1] == y[1]) return 0;
	if (x[1] > y[1]) {
		swap(x, y);
		swap(n, m);
	}
	int mn = 1e9, p = 0;
	for (int i = 1; i <= n; i++)
		mn = min(mn, x[i]);
	for (int i = 1; i <= n; i++)
		if (mn == x[i]) {
			p = i;
			break;
		}
	int mx = -1e9, q = 0;
	for (int i = 1; i <= m; i++)
		mx = max(mx, y[i]);
	for (int i = 1; i <= m; i++)
		if (mx == y[i]) {
			q = i;
			break;
		}
	int cp = 0, cq = 0;
	for (int i = 1; i <= p; i++)
		nx[++cp] = x[i];
	for (int i = 1; i <= q; i++)
		ny[++cq] = y[i];
	int ans = chk(cp, cq);
	cp = 0, cq = 0;
	for (int i = n; i >= p; i--)
		nx[++cp] = x[i];
	for (int i = m; i >= q; i--)
		ny[++cq] = y[i];
	ans &= chk(cp, cq);	
	return ans;
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> c >> n >> m >> q;
	for (int i = 1; i <= n; i++)
		cin >> x[i], xx[i] = x[i];
	for (int i = 1; i <= m; i++)
		cin >> y[i], yy[i] = y[i];
	int nn = n, mm = m;
		cout << kudo();
		for (int w = 1; w <= q; w++) {
			int nx, ny;
			cin >> nx >> ny;
			while (nx--) {
				int p, t;
				cin >> p >> t;
				x[p] = t;
			}
			while (ny--) {
				int p, t;
				cin >> p >> t;
				y[p] = t;
			}
			cout << kudo();
			n = nn, m = mm;
			for (int i = 1; i <= n; i++)
				x[i] = xx[i];
			for (int i = 1; i <= m; i++)
				y[i] = yy[i];
		}
	return 0;
}

标签:return,匹配,int,题解,拓展,P9870,序列,NOIP2023,dp
From: https://www.cnblogs.com/Rock-N-Roll/p/18548583

相关文章

  • Toyota Programming Contest 2024#11(AtCoder Beginner Contest 379)题解总结
    AtCoderBeginnerContest379Rated:\(770\)A-Cyclic简单模拟。B-Strawberries字符串模拟,substr函数秒了C-Repeating中等模拟。思路:判断是否合法很简单,重点在计算花费。假设我们是\(0\)号点有\(N\)个棋子,然后移动到每个点上,显然花费为\(\frac{N(N+1)}{......
  • [CEOI2023] The Ties That Guide Us 题解
    Description你用销售机器人的利润雇佣了一名助手,现在你准备好去拿走装有CEOI奖章的保险箱了。保险箱位于一所由\(n\)个房间所组成的大学建筑内,这些房间由\(n-1\)扇门连接。每个房间都可以从其他任何房间到达,且每个房间最多与\(3\)扇门相连。你和你的助手都有描述建筑物......
  • P1437 敲砖块 题解
    题意在一个凹槽中放置了\(n\)层砖块、最上面的一层有\(n\)块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示:14154323333376221311222331如果你想敲掉第\(i\)层的第\(j\)块砖的话,若\(i=1\),你可以直接......
  • [CEOI2023] Tricks of the Trade 题解
    Description有\(n\)个机器人排成一排,第\(i\)个机器人的购买价是\(a_i\)欧元,卖出价是\(b_i\)欧元。给定\(1\lek\len\),你需要购买一段长度至少为\(k\)的区间中所有的机器人,然后选择其中的恰好\(k\)个机器人来卖出。你需要求出:你能够得到的最大收益;在收益最大化......
  • P11276 第一首歌 题解
    P11276第一首歌题解一道简单的字符串题目。题目解释说求一个最短的字符串\(t\),使其满足对于给定的字符串\(s\):\(s\)为\(t\)的前缀。\(s\)为\(t\)的后缀。\(s\)不为\(t\)。仔细思考一下,则易得\(t\)的最短长度的最大值为\(s\)的两倍。即当\(s\)......
  • 读数据质量管理:数据可靠性与数据质量问题解决之道04收集与清洗
    1.      收集数据1.1.        数据收集和清洗是生产管道中的第一步1.1.1.          数据转换和测试则在生产管道中解决数据质量问题1.2.        在收集数据时,管道的任何地方可能都没有入口点重要,因为入口点是任何数据管道中最上游的位......
  • 题解:P11277 世界沉睡童话
    比较简单的构造。注意到题面给出\(a_i\le2n-1\)的条件,考虑这个有什么用,你会发现从\(n\)到\(2n-1\)这\(n\)个数都是两两互不为约数,所以当我们构造出序列后,这些数可以用来填补空位。\(k\)的上界是\(\frac{n(n-1)}{2}\),显然在全部都为同一个数的时候取到,显然有\(x\)个......
  • Atcoder ABC 216 G 01Sequence 题解 [ 蓝 ] [ 差分约束 ]
    01Sequence:比较板的差分约束,但有一个很妙的转化。朴素差分约束设\(x_i\)表示第\(i\)位的前缀和。我们要最小化\(1\)的个数,就要求最小解,就要求最长路。因为约束条件都是大于等于号,所以求最长路才能满足所有条件。求最大解也是同理。我们可以对于每一个条件,列出如下不等式......
  • 2024年09月CCF-GESP编程能力等级认证Python编程二级真题解析
    本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。一、单选题(每题2分,共30分)第1题据有关资料,山东大学于1972年研制成功DJL-1计算机,并于1973年投入运行,其综合性能居当时全国第三位。DJL-1计算机运算控制部分所使用的......
  • [CF1188E] Problem from Red Panda 题解
    [CF1188E]ProblemfromRedPanda题解考虑每个位置的操作次数\(c_i\),不难发现,\(i\)气球最后的颜色个数\(d_i\)是\(a_i+c_ik-\sumc_i\),如果存在\(\forallc_i>0\),那么我们总是可以把所有气球少操作一次,这样上式不变,不影响最后的序列,下文所有的操作序列都假设\(\min......