【codeforces 1217 E】Sum Queries?(结论,线段树)

文章目录

题意

s u m sum sum为一个多重集的所有元素的和

我们这样定义一个多重集是平衡的:

对于 s u m sum sum的每一个数位,多重集中至少有一个元素与 s u m sum sum此数位相同

现在,给你一个a数组,有两种操作

  1. 修改a数组中一个位置的值

  2. 询问 [ l , r ] [l,r] [l,r]区间所有数构成的集合的子集中所有不平衡多重集的 s u m sum sum的最小值(不一定要由连续元素构成)

思路

空集是平衡集,只有一个元素的集合也是平衡集。

然后尝试向其中加入元素,发现只要不出现两个数在某一位上都非零,那这个集合仍旧平衡,否则就不平衡了。

然后题解中的证明我觉得没有必要。只用说明一个元素个数大于2的不平衡集一定可以有一个大小为2的不平衡子集,因为一定存在两个数某位都非零,把这两个取出来就好了。

那么只需要考虑大小为2的集合就行了。问题就转化为了在区间里找两个数,这两个数有至少一位都非零,并且和最小。使用线段树维护。

我看错题意看成必须要连续区间,貌似还需要一个求和的线段树再加上9个set。。。死了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, E = 10, inf = 2e9 + 7;
struct Min_tr{
	#define ls (u<<1)
	#define rs (u<<1^1)
	int t[N<<2][E], ans[N<<2];
	void push_up(int u){
		ans[u] = min(ans[ls], ans[rs]);
		for (int i = 0; i < E; ++ i){
			t[u][i] = min(t[ls][i], t[rs][i]);
			if (t[ls][i] < inf && t[rs][i] < inf)
				ans[u] = min(ans[u], t[ls][i] + t[rs][i]);
		}
	}
	void chg(int u, int x){
		int tmp = x;
		for (int i = 0; i < E; ++ i){
			if (tmp % 10) t[u][i] = x;
			else t[u][i] = inf;
			tmp /= 10;
		}
		ans[u] = inf;
	}
	void build(int u, int l, int r, int *a){
		if (l == r){
			chg(u, a[l]);
			return;
		}
		int mid = l + r >> 1;
		build(ls, l, mid, a);
		build(rs, mid+1, r, a);
		push_up(u);
	}
	void modify(int u, int l, int r, int P, int X){
		if (l == r){
			chg(u, X);
			return;
		}
		int mid = l + r >> 1;
		if (P <= mid) modify(ls, l, mid, P, X);
		else modify(rs, mid+1, r, P, X);
		push_up(u);
	}
	pair<int, int> merge(pair<int, int> x, pair<int, int> y){
		pair<int, int> ret;
		ret.first = min(x.first, y.first);
		ret.second = min(x.second, y.second);
		if (x.second < inf && y.second < inf)
			ret.first = min(ret.first, x.second + y.second);
		return ret;
	}
	pair<int, int> query(int u, int l, int r, int L, int R, int X){
		if (L <= l && r <= R)
			return make_pair(ans[u], t[u][X]);
		int mid = l + r >> 1;
		pair<int, int> ret = make_pair(inf, inf);
		if (L <= mid) ret = merge(ret, query(ls, l, mid, L, R, X));
		if (mid < R) ret = merge(ret, query(rs, mid+1, r, L, R, X));
		return ret;
	}
	#undef ls
	#undef rs
}t;
int n, q, a[N];

int main()
{
	ios::sync_with_stdio(false);
	cin >> n >> q;
	for (int i = 1; i <= n; ++ i)
		cin >> a[i];
	t.build(1, 1, n, a);
	for (int i = 1; i <= q; ++ i){
		int opt, x, y;
		cin >> opt >> x >> y;
		if (opt == 1){
			t.modify(1, 1, n, x, y);
		}
		else{
			int ans = inf;
			for (int i = 0; i < E; ++ i)
				ans = min(ans, t.query(1, 1, n, x, y, i).first);
			if (ans == inf) cout << -1 << endl;
			else cout << ans << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值