Sum Queries? CodeForces - 1217E (线段树)

Sum Queries? CodeForces - 1217E (线段树)

题意:

定义一个集合为\(balanced\)的,当且仅当集合内数字之和的每个十进制位,都与集合中某个数该位相同。否则,称该集合为\(unbalanced\)的。

给定一个长度为\(n\)的序列,\(q\)次询问一个区间内数字之和最小的\(unbalanced\)集合,输出数字之和。若没有输出\(-1\)。

\(n,q<=200000\)。

题解:

可以发现,如果存在\(unbalanced\)集合,那么最小的一定是只两个数组成的集合。这两个数存在一个十进制位不都为\(0\)。

建立十棵线段树,维护每个位置上该位非\(0\)的区间最小值,更新最小值时更新答案。如果查询时,如果区间需要分开查询,不要忘了分开之后的多个区间之间的数也是会相互影响的,应该枚举每个十进制位,将多个区间的最小值合并来更新答案。

时间复杂度\(O(10×(n+m)log(n))\)。

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
const int inf = 2e9 + 10;
const int maxn = 2e5 + 10;

struct Node {
    int l, r, Min[12];
}t[maxn*4];
int Ans[maxn*4], a[maxn], tmp[maxn];
int ans;

void push_up(int id) {
    Ans[id] = inf;
    for (int i = 1; i <= 10; i++) {
        if (t[id*2].Min[i] < inf && t[id*2+1].Min[i] < inf)
            Ans[id] = min(Ans[id], t[id*2].Min[i] + t[id*2+1].Min[i]);
        t[id].Min[i] = min(t[id*2].Min[i], t[id*2+1].Min[i]);
    }
    Ans[id] = min(Ans[id], min(Ans[id*2], Ans[id*2+1]));
}

void build(int id, int l, int r) {
    Ans[id] = inf;
    t[id].l = l, t[id].r = r;
    if (l == r) {
        int res = a[l];
        for (int i = 1; i <= 10; i++) {
            if (res % 10 == 0) t[id].Min[i] = inf;
            else t[id].Min[i] = a[l];
            res /= 10;
        }
        return;
    }
    int mid = (l+r) / 2;
    build(id*2, l, mid), build(id*2+1, mid+1, r);
    push_up(id);
}

void update(int id, int l, int x) {
    if (t[id].l == l && t[id].r == l) {
        int res = x;
        for (int i = 1; i <= 10; i++) {
            if (res % 10 == 0) t[id].Min[i] = inf;
            else t[id].Min[i] = x;
            res /= 10;
        }
        return;
    }
    int mid = (t[id].l + t[id].r) / 2;
    if (l <= mid) update(id*2, l, x);
    else update(id*2+1, l, x);
    push_up(id);
}

void query(int id, int l, int r) {
    if (l <= t[id].l && r >= t[id].r) {
        for (int i = 1; i <= 10; i++) {
            if (tmp[i] < inf && t[id].Min[i] < inf) {
                ans = min(ans, tmp[i] + t[id].Min[i]);
            }
            tmp[i] = min(tmp[i], t[id].Min[i]);
        }
        ans = min(ans, Ans[id]);
        return;
    }
    int mid = (t[id].l + t[id].r) / 2;
    if (r <= mid) query(id*2, l, r);
    else if (l > mid) query(id*2+1, l, r);
    else query(id*2, l, mid), query(id*2+1, mid+1, r);
}

int n, m, res;
int main() {
    //fopi;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        if (op == 1) update(1, x, y);
        else {
            for (int i = 1; i <= 10; i++) tmp[i] = inf;
            ans = inf;
            query(1, x, y);
            printf("%d\n", ans >= inf ? -1 : ans);
        }
    }
}

原文地址:https://www.cnblogs.com/ruthank/p/11514797.html

时间: 2024-12-27 13:22:32

Sum Queries? CodeForces - 1217E (线段树)的相关文章

Mass Change Queries CodeForces - 911G (线段树合并)

链接 大意: 给定序列, 每次操作将区间[l,r]中的x全改为y, 最后输出序列 权值范围比较小, 对每个权值开一颗线段树, 每次将x合并到y上即可 #include <iostream> #include <algorithm> #include <cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) #define mid (l+r>>1) #define lc (o<<1) #define

Codeforces 384E 线段树+dfs序

题目链接:点击打开链接 题意: 给定n个点,m个询问的无向树(1为根) 下面n个数表示每个点的权值 下面n-1行给出树 操作1:x点权值+v, x的第 i & 1 的儿子-v, 第 !(i&1) 的儿子+v 操作2:询问x点权值 dfs把树转成序列 根据深度把点分成2组 分别用线段树维护.. 然后Y一下 #include<stdio.h> #include<string.h> #include<iostream> #include<algorith

spoj gss2 : Can you answer these queries II 离线&amp;&amp;线段树

1557. Can you answer these queries II Problem code: GSS2 Being a completist and a simplist, kid Yang Zhe cannot solve but get Wrong Answer from most of the OI problems. And he refuse to write two program of same kind at all. So he always failes in co

SPOJ GSS5 Can you answer these queries V (线段树)

原来有一两个人说我不帅的时候,我不以为意,逗我玩而已,后来几乎所有 人都说我不帅,我才真正意识到事态的严重,这社会骗子真是越来越多了... 好吧我承认,这个笑话不好笑,其实我想说的是,做人一定要坚持自己的原则, 哪怕有一天所有人都和你背道而驰,都不要放弃自己当初的梦想,如果有一天, 我们淹没在人海之中,庸碌一生,那是因为我们不够努力,不够勇敢的去面对生活. 每天积累一点点,嗯,满足简单的快乐. ---------------------------------------------------

Codeforces 446C 线段树 递推Fibonacci公式

聪哥推荐的题目 区间修改和区间查询,但是此题新颖之处就在于他的区间修改不是个定值,而是从L 到 R 分别加 F1.F2....Fr-l+1 (F为斐波那契数列) 想了一下之后,觉得用fib的前缀和来解决,每次做懒惰标记记录下当前区间是从哪个L开始加起的,敲了一半之后发现有问题,就跟上次遇到的懒惰标记问题一样,这是个覆盖性的懒惰标记,每次向下传递后,都要先清除孩子的,清除孩子的也有可能要清除son's son,所以要一直pushdown下去,否则就会错,但这样就会超时. 能不能有个累加型的标记让我

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变一个数的值(注意不是真的改变),使得这个区间的gcd是小明所猜的数也算小明猜对.另一种操作就是真的修改某一点的值. 解题思路 这里我们使用线段树,维护区间内的gcd,判断的时候需要判断这个区间的左右子区间的gcd是不是小明猜的数的倍数或者就是小明猜的数,如果是,那么小明猜对了.否则就需要进入这个区间

Educational Codeforces Round 37-F.SUM and REPLACE (线段树,线性筛,收敛函数)

F. SUM and REPLACE time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output Let D(x) be the number of positive divisors of a positive integer x. For example, D(2)?=?2 (2 is divisible by 1 and 2), D(6)?

CodeForces - 920F SUM and REPLACE (线段树)

题意:给N个数M次操作,(1<=N,M<=3e5, 1<=ai<=1e6),1是使[L,R]中的每个元素变成其因子的个数之和:2是求[L,R]区间之和 分析:看上去就很线段树的一题,但是却思考了很久.发现1和2即使对其,也不会改变二者的值.而且一个大于2的数进行多次1操作,也最终会退化到2. 先预处理筛出1e6以内各数的质因子个数和.在线段树的节点中维护两个值:区间和以及区间最大值.在update函数中,如果该区间的最大值不超过2,那么该区间没有更新的必要:若超过2,则递归向下找到

【BZOJ-3638&amp;3272&amp;3267&amp;3502】k-Maximum Subsequence Sum 费用流构图 + 线段树手动增广

3638: Cf172 k-Maximum Subsequence Sum Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 174  Solved: 92[Submit][Status][Discuss] Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. Input The first line contains integer n (1 ≤ n