KDOI-3还原数据题解

「KDOI-03」还原数据

题目描述

小 E 正在做一道经典题:

给定一个长度为 \(n\) 的序列 \(a\)\(q\) 个操作,操作共有 \(2\) 种类型:

  • \(\tt{1~l~r~x}\):对于所有 \(l\le i\le r\)\(a_i\leftarrow a_i+x\)
  • \(\tt{2~l~r~x}\):对于所有 \(l\le i\le r\)\(a_i\leftarrow \max(a_i,x)\)

题目要求输出所有操作结束后的最终序列 \(a'\)

小 E 迅速写了一份代码提交,但是发现,由于宇宙射线的影响,输入数据出现了一些小问题。具体地,对于所有 \(2\) 操作,操作中给出的 \(x\) 均被丢失了,也就是说,输入数据中的 \(2\) 操作只剩下了 \(\tt{2~l~r}\)。输出数据则没有问题。小 E 现在想要通过剩余的数据恢复原来的输入数据,请你帮助他完成这个任务。

当然,可能会有多种合法的输入数据,你需要找到其中任意一种。数据保证有解。

输入格式

从标准输入读入数据。

本题有多组测试数据。

第一行一个正整数 \(T\),表示数据组数。

对于每组测试数据,第一行两个非负整数 \(n,q\)

第二行 \(n\) 个整数,表示初始序列 \(a_1,a_2,\ldots,a_n\)

接下来 \(q\) 行,每行一次操作,形如 \(\tt{1~l~r~x}\)\(\tt{2~l~r}\)

接下来一行 \(n\) 个整数,表示最终序列 \(a_1',a_2',\ldots,a_n'\)

输出格式

输出到标准输出。

本题开启自定义校验器(Special Judge)。

对于每组测试数据,设共有 \(q_2\)\(2\) 操作,输出一行 \(q_2\) 个整数,第 \(i\) 个整数表示第 \(i\)\(2\) 操作中所给出的 \(x\) 的值。

你需要保证 \(-10^{15}\le x\le 10^{15}\)

样例 #1

样例输入 #1

1
5 3
1 2 3 4 5
2 3 5
1 3 4 2
2 1 1
20 2 5 6 5

样例输出 #1

3 20

提示

【样例 1 解释】

所有合法输出需要满足:第 \(1\) 个数 \(\le3\),第 \(2\) 个数恰好为 \(20\)

【样例 2】

见选手文件中的 restore/restore2.inrestore/restore2.ans

【样例 3】

见选手文件中的 restore/restore3.inrestore/restore3.ans


【数据范围】

\(q_2\) 为单组数据内 \(2\) 操作的个数,\(\sum n\) 为单个测试点内所有 \(n\) 的和,\(\sum q\) 为单个测试点内所有 \(q\) 的和。

对于 \(20\%\) 的数据,保证 \(n,q\le50\)\(\sum n,\sum q\le1~000\)

对于 \(40\%\) 的数据,保证 \(n,q\le1~000\)\(\sum n,\sum q\le10^5\)

对于另外 \(20\%\) 的数据,保证 \(l=1,r=n\)

对于另外 \(20\%\) 的数据,保证 \(q_2\le100\)

对于 \(100\%\) 的数据,保证 \(1\le T\le 100\)\(1\le n,q\le 10^5\)\(1\le\sum n,\sum q\le 3\times10^5\)\(-10^9\le a_i,x\le 10^9\)\(-10^{15}\le a_i'\le10^{15}\)\(q_2\ge1\)


【校验器】

本题样例文件较大,无法在附件中下载,请在选手文件中查看。

为了方便测试,在 \(\texttt{restore}\) 目录下我们下发了 \(\texttt{checker.cpp}\) 文件。你可以编译该文件,并使用它校验自己的输出文件。请注意它与最终评测时所用的校验器并不完全一致,你不需要也不应该关心其代码的具体内容。

编译命令为:

g++ checker.cpp -o checker -std=c++14

使用方式为:

./checker <inputfile> <outputfile> <answerfile>

校验器可能会返回以下状态中的其中一种:

  • \(\tt{Accepted}\):表示你的输出完全正确。
  • \(\tt{Wrong~answer~at~testcase~ x}\):表示你的输出在第 \(x\) 个测试数据出错。

【提示】

本题输入输出量较大,推荐使用较快的输入输出方式。

KDOI 出题组温馨提示:多测不清空,爆零两行泪。

题解

首先因为\(max\)操作无法被后面的抵消,倒序考虑操作,区间加改为减

引理:对于一次max操作,这个值\(k\)一定小于等于区间\([l,r]\)的最小值

证明,反证法,如果不是这样,那么取一个更大的数字,区间的最小值在经过了后面的操作之后会大于最终值

那么因为数据保证有解,我们就可以取等号,那么问题显而易见,我们开一棵维护区间最小值和区间加的线段树,倒序对于区间加改为减,每次max操作输出区间最小值即可

//fc D:\编程\C++\restore3.ans D:\编程\C++\restore3.out 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 505500
//#define int long long
#define ll long long 
struct node {
	int l, r;ll mn, add;
}t[N << 2];
struct ask {
	int id, l, r;ll x;
}a[N];
#define lc x<<1
#define rc x<<1|1
int s[N], n, m, q, tot;
ll ans[N];
void build(int l,int r,int x) {
	int mid = l + r >> 1;
	t[x] = { l,r,0x3f3f3f3f3f3f3f3f,0 };
	if (l == r) {
		scanf("%lld", &t[x].mn);
		return;
	}
	build(l, mid, lc);
	build(mid + 1, r, rc);
	t[x].mn = min(t[lc].mn, t[rc].mn);
}
inline void pushup(int x) {
	t[x].mn = min(t[lc].mn, t[rc].mn);
}
inline void pushdown(node& a, node& b, node& c) {
	b.add += a.add, c.add += a.add;
	b.mn += a.add, c.mn += a.add;
	a.add = 0;
}
inline void pushdown(int x) {
	pushdown(t[x], t[lc], t[rc]);
}
void change(int l, int r, int x, ll k) {
	if (l <= t[x].l && t[x].r <= r) {
		t[x].add += k;
		t[x].mn += k;
		return;
	}
	pushdown(x);
	int mid = t[x].l + t[x].r >> 1;
	if (l <= mid)change(l, r, lc, k);
	if (mid < r)change(l, r, rc, k);
	pushup(x);
}
ll find(int l, int r, int x) {
	if (l <= t[x].l && t[x].r <= r) {
		return t[x].mn;
	}
	pushdown(x);
	ll ans = 0x3f3f3f3f3f3f3f3f;
	int mid = t[x].l + t[x].r >> 1;
	if (l <= mid)ans = min(ans, find(l, r, lc));
	if (mid < r)ans = min(ans, find(l, r, rc));
	pushup(x);
	return ans;
}
void init() {
	tot = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)scanf("%d", &s[i]);
	for (int i = 1; i <= m; i++) {
		int op, l, r;ll k;
		scanf("%d%d%d", &op, &l, &r);
		if (op == 1) {
			scanf("%lld", &k);
			a[i] = { op,l,r,k };
			continue;
		}
		a[i] = { op,l,r,-1ll };
	}
	build(1, n, 1);
	for (int i = m; i > 0; i--) {
		if (a[i].id == 1) {
			change(a[i].l, a[i].r, 1, -a[i].x);
			continue;
		}
		ans[++tot] = find(a[i].l, a[i].r, 1);
	}
	for (int i = tot; i; i--) {
		printf("%lld ", ans[i]);
	}
	puts("");
}
signed main() {
//	freopen("restore2.in","r",stdin);
//	freopen("restore2.out","w",stdout);
	int t;
	scanf("%d", &t);
	while (t--) {
		init();
	}
	return 0;
}
posted @ 2022-11-30 22:47  spdarkle  阅读(43)  评论(0编辑  收藏  举报