KDOI-3还原数据题解

「KDOI-03」还原数据

题目描述

小 E 正在做一道经典题:

给定一个长度为 n 的序列 aq 个操作,操作共有 2 种类型:

  • 1 l r x:对于所有 liraiai+x
  • 2 l r x:对于所有 liraimax(ai,x)

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

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

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

输入格式

从标准输入读入数据。

本题有多组测试数据。

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

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

第二行 n 个整数,表示初始序列 a1,a2,,an

接下来 q 行,每行一次操作,形如 1 l r x2 l r

接下来一行 n 个整数,表示最终序列 a1,a2,,an

输出格式

输出到标准输出。

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

对于每组测试数据,设共有 q22 操作,输出一行 q2 个整数,第 i 个整数表示第 i2 操作中所给出的 x 的值。

你需要保证 1015x1015

样例 #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 个数 3,第 2 个数恰好为 20

【样例 2】

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

【样例 3】

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


【数据范围】

q2 为单组数据内 2 操作的个数,n 为单个测试点内所有 n 的和,q 为单个测试点内所有 q 的和。

对于 20% 的数据,保证 n,q50n,q1 000

对于 40% 的数据,保证 n,q1 000n,q105

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

对于另外 20% 的数据,保证 q2100

对于 100% 的数据,保证 1T1001n,q1051n,q3×105109ai,x1091015ai1015q21


【校验器】

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

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

编译命令为:

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

使用方式为:

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

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

  • Accepted:表示你的输出完全正确。
  • 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 @   spdarkle  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示