CF1217E Sum Queries?(线段树 单点修改)

此题的难点在于证明,任何一个不平衡的子集一定包含至少一个由两个元素组成的不平衡的集合。

我采用反证法加以证明。假设一个不平衡U的集合由n(n>2)的元素组成,且其所有的两个元素的子集均平衡。首先,两个元素的集合要想平衡,两个元素对应十进制数位上的数字,至少有一个要为0。因此集合U中要想达到所有的两个元素子集均平衡,则每个十进制数位上,至少有n-1个元素在该数位上的数字为0(此处容易证明,留给读者自行证明)。也就是说集合U的n个元素,每个十进制数位上,最多有一个元素在该数位上的数字不为0,这样n个元素的和在该为上的数字要么是0,要么是这个不为0的数字。所有数位都满足这种条件,因此对于集合U来说,必定平衡。与假设矛盾。

因此,任何一个不平衡的子集一定包含至少一个由两个元素组成的不平衡的集合。我们的任务就转变成了寻找由两个元素组成的不平衡的集合的元素和的最小值。

题目的修改操作时对于一个元素进行修改,因此建立一颗单点修改的线段树,即可完成此题。线段树结点内存储的信息为,1)结点区间对应的最优结果best;2)一个数组t[i],存储结点对应的区间,在十进制数位的第i位所有不为0的所有元素中,最小的元素。时间复杂度为O(9*(m+n)*logn)。
具体实现详见代码,个人觉得比较容易理解。
题目链接 CF1217E Sum Queries?

//By querry
#include <bits/stdc++.h>
#pragma warning (disable:4996)
#define INF 2e9
#define MOD 998244353
typedef long long ll;
using namespace std;

int n, m;
typedef struct node {
	int t[10], best;
	node() {
		for (int i = 1; i <= 9; i++) t[i] = INF;
		best = INF;
	}
}node;

template <typename T>
class SegmentTree_Point {
public:
	T *info;
	int *list;
	SegmentTree_Point() {
		info = new T[800005];
		list = new int[200005];
	}
	void build() {
		for (int i = 1; i <= n; i++)
			update(i, list[i]);//O(9logn)
	}
	void update(int p, int v, int o = 1, int L = 1, int R = n) {//O(9logn)
		int M = (L + R) / 2;
		if (L == R) {
			int temp = v;
			for (int i = 1; i <= 9; i++) {
				if (temp % 10)
					info[o].t[i] = v;
				else info[o].t[i] = INF;
				temp /= 10;
			}
		}
		else {
			if (p <= M) update(p, v, o * 2, L, M);
			else update(p, v, o * 2 + 1, M + 1, R);
			info[o] = merge(info[2 * o], info[2 * o + 1]);
		}
	}
	T merge(T &a, T &b) {
		T c;
		c.best = min(a.best, b.best);
		for (int i = 1; i <= 9; i++) {
			c.t[i] = min(a.t[i], b.t[i]);
			if (a.t[i] < INF && b.t[i] < INF)
				c.best = min(c.best, a.t[i] + b.t[i]);
		}
		return c;
	}
	T query(int ql, int qr, int o = 1, int L = 1, int R = n) {//O(2logn)
		int M = (L + R) / 2;
		T a, b;
		if (ql <= L && R <= qr) return info[o];
		if (ql <= M) a = query(ql, qr, o * 2, L, M);
		if (M < qr) b = query(ql, qr, o * 2 + 1, M + 1, R);
		return merge(a, b);
	}
};

int main() {
	scanf("%d%d", &n, &m);
	SegmentTree_Point <node> ST;
	for (int i = 1; i <= n; i++)
		scanf("%d", &ST.list[i]);
	ST.build();//O(n*9logn)
	for (int i = 1; i <= m; i++) {
		int t, x, y;
		scanf("%d%d%d", &t, &x, &y);
		if (t == 1) ST.update(x, y);//O(9logn)
		else {
			node ret = ST.query(x, y);//O(2logn)
			if (ret.best < INF) printf("%d\n", ret.best);
			else printf("-1\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值