您的位置:首页 > 其它

51nod-1394 差和问题(树状数组)

2016-11-04 20:34 239 查看
原题链接

1394 差和问题


基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题


 收藏


 关注

有一个多重集合S(即里面元素可以有重复),初始状态下有n个元素,对他进行如下操作:

1、向S里面添加一个值为v的元素。输入格式为1 v

2、向S里面删除一个值为v的元素。输入格式为2 v

3、询问S里面的元素两两之差绝对值之和。输入格式为3

 

对于样例,

操作3,|1-2|+|1-3|+|2-3|=4

操作1 4之后,集合中的数字为1 2 3 4

操作3,|1-2|+|1-3|+|2-3|+|1-4|+|2-4|+|3-4|=10

操作2 2之后,集合中的数字为1 3 4

操作3,|1-3|+|1-4|+|3-4|=6

Input
第一行输入两个整数n,Q表示集合中初始元素个数和操作次数。(1<=n,Q<=100,000)
第二行给出n个整数a[0],a[1],a[2],…,a[n-1],表示初始集合中的元素。(0<=a[i]<=1,000,000,000) 
接下来Q行,每行一个操作。(0<=v<=1,000,000,000)


Output
对于第2类操作,如果集合中不存在值为v的元素可供删除,输出-1。
对于第3类操作,输出答案。


Input示例
3 5
1 2 3
3
1 4
3
2 2
3


Output示例
4
10
6


某大牛思路:

先思考一个简单的问题,给定N个数字,求两两之间差的绝对值之和。
这个很明显先排个序,这样就可以把绝对值去掉了。然后从小到大统计每一个和其它组成的差绝对值之和。
再联系到本题中。N+Q<=2e5,那么先把所有的数据读入,最多有2e5种数字,离散化一下,对应到树状数组之中。插入和删除一个数字的时候要统计一下这个对答案的影响。
当前数字为x,比当前数字小的有cnt个,总和为sum,那么这一部分对答案的影响是x*cnt-sum.对于求比当前数字大的数字类似的道理。
总的复杂度是(n+Q)log(n+Q)。
以后记住了,这种增删查的树状数组很方便。。


#include <bits/stdc++.h>
#define maxn 100005
#define MOD 1000000007
using namespace std;
typedef long long ll;

struct Node{
int t, v;
}node[maxn];
ll sum[maxn<<1], p1[maxn<<1];
int num[maxn], k[maxn<<1], cnt[maxn<<1], p2[maxn<<1];
int d;
void Update(int j, int e, ll p){
while(j <= d){
p2[j] += e;
p1[j] += p;
j += j & -j;
}
}
void Query(int j, int &s, ll &a){
a = sum[j];
s = cnt[j];
while(j){
a += p1[j];
s += p2[j];
j -= j & -j;
}
}
int Get(int v){
return lower_bound(k+1, k+1+d, v) - k;
}
void solve(int j, ll &ans, int s){
ll a = 0, b = 0;
int m1 = 0, m2 = 0;
Query(j, m1, a);
Query(d, m2, b);
ans += (b - a - (ll)k[j] * (m2 - m1)) * s;
a = 0, m1 = 0;
Query(j-1, m1, a);
ans += ((ll)k[j] * m1 - a) * s;
}
int main(){

// freopen("in.txt", "r", stdin);
int n, q, m = 0;
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++){
scanf("%d", num+i);
k[++m] = num[i];
}
for(int i = 0; i < q; i++){
scanf("%d", &node[i].t);
if(node[i].t != 3){
scanf("%d", &node[i].v);
if(node[i].t == 1)
k[++m] = node[i].v;
}
}
sort(k+1, k+1+m);
d = unique(k+1, k+1+m) - k - 1;
for(int i = 1; i <= n; i++){
int e = lower_bound(k+1, k+1+d, num[i]) - k;
cnt[e]++;
sum[e] += num[i];
}
for(int i = 1; i <= d; i++){
sum[i] += sum[i-1];
cnt[i] += cnt[i-1];
}
ll ans = 0;
for(int i = 1; i <= d; i++){
if(cnt[i] == cnt[i-1])
continue;
ans += (sum[d] - sum[i] - (cnt[d] - cnt[i]) * (ll)k[i]) * (cnt[i] - cnt[i-1]);
}
for(int i = 0; i < q; i++){
if(node[i].t == 3){
printf("%I64d\n", ans);
}
else if(node[i].t == 1){
int c = Get(node[i].v);
Update(c, 1, node[i].v);
solve(c, ans, 1);
}
else{
int c = Get(node[i].v);
if(k[c] != node[i].v){
puts("-1");
continue;
}
int a = 0;
ll b = 0;
Query(c, a, b);
int q = 0;
b = 0;
Query(c-1, q, b);
if(a == q){
puts("-1");
continue;
}
Update(c, -1, -node[i].v);
solve(c, ans, -1);
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: