FJUT3574 HOME_W的附加题(带权线段树)题解
Posted kirinsb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FJUT3574 HOME_W的附加题(带权线段树)题解相关的知识,希望对你有一定的参考价值。
题意:
给定n个数a1,a2,a3,……an。和m次操作。
每次操作格式如下
x y k 表示将a[x]替换为y。并求替换后,前k小的数之和
思路:我们用带权线段树维护权值,这里就是维护i的个数num[i],然后顺便维护一下和。每次查询前k个数求和。
练习赛题解:
代码:
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn = 100000 + 10; const int seed = 131; const ll MOD = 1e9 + 7; const ll INF = 1e17; using namespace std; int num[maxn << 2], a[maxn], b[maxn]; ll sum[maxn << 2]; void build(int l, int r, int rt){ if(l == r){ num[rt] = a[l]; sum[rt] = l * num[rt]; return; } int m = (l + r) >> 1; build(l, m, rt << 1); build(m + 1, r, rt << 1 | 1); num[rt] = num[rt << 1] + num[rt << 1 | 1]; sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } void update(int pos, int l, int r, int rt, int v){ if(l == r){ num[rt] += v; sum[rt] = l * num[rt]; return; } int m = (l + r) >> 1; if(pos <= m) update(pos, l, m, rt << 1, v); else update(pos, m + 1, r, rt << 1 | 1, v); num[rt] = num[rt << 1] + num[rt << 1 | 1]; sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } ll query(int l, int r, int rt, int k){ if(l == r){ return l * k; } int m = (l + r) >> 1; ll ans = 0; if(num[rt << 1] >= k){ ans += query(l, m, rt << 1, k); } else{ ans += sum[rt << 1]; ans += query(m + 1, r, rt << 1 | 1, k - num[rt << 1]); } return ans; } int main(){ int n, m; memset(a, 0, sizeof(a)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++){ scanf("%d", &b[i]); a[b[i]]++; } build(1, 100000, 1); int x, y, k; for(int i = 0; i < m; i++){ scanf("%d%d%d", &x, &y, &k); update(b[x], 1, 100000, 1, -1); update(y, 1, 100000, 1, 1); b[x] = y; printf("%lld ", query(1, 100000, 1, k)); } return 0; }
以上是关于FJUT3574 HOME_W的附加题(带权线段树)题解的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 666E Forensic Examination SAM+权值线段树