离线+线段树 HDU4228

题目大意:给一个数组a,他的顺序是严格的单调增,然后有如下三个操作

①加入一个val到a数组里面去,加入的位置就是a[i-1]<val<a[i+1]

②删除一个a[i]=val的值

③查询所有下标i%5=3的值

思路:线段树+离线

首先因为线段树中不支持添加、删除操作的,所以只能离线把所有的val离散化以后放到区间里面去。然后关键就是线段树是怎么建立的。

我们知道,每个%5都会有0,1,2,3,4这5个值,然后我们可以通过线段树来维护这5个值。我们首先用sum[5]表示能被5整除的5种求余后不同类型的数的val,然后再用cnt记录当前这个区间里面还存在的数值。接下来我们定义父亲区间,左子区间和右子区间,然后我们发现左子区间的区间范围和父亲区间的是一样的,然后右子区间的范围要发生改变,他的位置改变到如下位置:(i+cnt)%5,其中i表示右子区间的第几个区间,然后cnt表示左子区间的有效的个数。然后我们就这样去维护就好啦。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
const int maxn = 1e5 + 5;
struct point{
    LL sum[5];
    int cnt;
    void init(){
        this -> cnt = 0;
        memset(sum, 0, sizeof(sum));
    }
}tree[maxn << 2];
LL a[maxn];
int q;
pair<char, LL> ch[maxn];

void buildtree(int o, int l, int r){
    if (l == r){
        tree[o].init();
        return ;
    }
    int mid = (l + r) / 2;
    if (l <= mid) buildtree(o << 1, l, mid);
    if (r > mid) buildtree(o << 1 | 1, mid + 1, r);
    tree[o].init();
}

inline void push_up(int o){
    tree[o].cnt = tree[o << 1].cnt + tree[o << 1 | 1].cnt;
}

void display(int o, int l, int r){
    printf("o = %d l = %d r = %d\n", o, l, r);
    for (int i = 0; i < 5; i++) printf("%d ", tree[o].sum[i]);
    printf("\n");
}

void update(int o, int l, int r, int pos, bool flag){
    if (l == r && l == pos){
        if (flag) {tree[o].sum[1] += a[pos]; tree[o].cnt = 1;}
        else {tree[o].sum[1] -= a[pos]; tree[o].cnt = 0;}
        return ;
    }
    int mid = (l + r) / 2;
    if (pos <= mid) update(o << 1, l, mid, pos, flag);
    if (pos > mid) update(o << 1 | 1, mid + 1, r, pos, flag);
    memset(tree[o].sum, 0, sizeof(tree[o].sum));
    for (int i = 0; i < 5; i++){
        int j = (i + tree[o << 1].cnt) % 5;
        tree[o].sum[i] += tree[o << 1].sum[i];
        tree[o].sum[j] += tree[o << 1 | 1].sum[i];
    }
    ///display(o, l, r);
    push_up(o);
    return ;
}

int main(){
    while (scanf("%d", &q) == 1 && q){
        int n = 0;
        for (int i = 1; i <= q; i++){
            char s[4]; LL tmp = -1;
            scanf("%s", s);
            if (s[0] == ‘d‘ || s[0] == ‘a‘) scanf("%I64d", &tmp);
            ch[i] = mk(s[0], tmp);
            if (s[0] == ‘a‘) a[++n] = tmp;
        }
        sort(a + 1, a + 1 + n);///有待商榷
        buildtree(1, 1, n);
        for (int i = 1; i <= q; i++){
            pair<char, LL> p = ch[i];
            if (p.first == ‘s‘){
                printf("%I64d\n", tree[1].sum[3]);
                continue;
            }
            else {
                int pos = lower_bound(a + 1, a + 1 + n, p.second) - a;
                update(1, 1, n, pos, p.first == ‘a‘);
            }
        }
    }
    return 0;
}

时间: 2024-10-24 00:31:24

离线+线段树 HDU4228的相关文章

hdu 4638 Group(莫队算法|离线线段树)

Group Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1323    Accepted Submission(s): 703 Problem Description There are n men ,every man has an ID(1..n).their ID is unique. Whose ID is i and i-

HDU 4417 Super Mario(离线线段树or树状数组)

Problem Description Mario is world-famous plumber. His "burly" figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss's castle as a l

UPC 2224 Boring Counting (离线线段树,统计区间[l,r]之间大小在[A,B]中的数的个数)

题目链接:http://acm.upc.edu.cn/problem.php?id=2224 题意:给出n个数pi,和m个查询,每个查询给出l,r,a,b,让你求在区间l~r之间的pi的个数(A<=pi<=B,l<=i<=r). 参考链接:http://www.cnblogs.com/zj62/p/3558967.html #include <iostream> #include <cstdio> #include <cstring> #incl

27号的十道离线线段树

27号的十道离线线段树 hdu4288: (2012成都网络赛&&cf) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const

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

HDU ACM 4417 Super Mario 离线线段树

分析:离线线段树,把所有询问离线读入,然后按H从小到大排序.对于所有结点也按从小到大排序,然后根据查询的H,将比H小的点加入到线段树,最后就是一个区间求和.这题貌似也可以用划分树,树状数组等方法做. #include<iostream> #include<algorithm> using namespace std; #define N 100005 struct Tree { int left,right,cnt; } TREE[N<<2]; struct Query

2333: [SCOI2011]棘手的操作[离线线段树]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2325  Solved: 909[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有

bzoj3082: Graph2 离线+线段树

经典问题.强制在线的话非常复杂. 考虑离线. 每条边的存在时间是一个区间,因此按时间建立一颗线段树,将每条边插入,拆成log条边.然后dfs线段树,每次并查集合并当前节点的所有边,到叶子节点时回答询问,回溯时撤销并查集的修改. 带撤销的并查集不能路径压缩,要按秩合并. #include<bits/stdc++.h> #define N 200005 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1)

ZOJ 5332 Calculation(离线 + 线段树)

题目链接 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5332 比赛的时候没有做出来,赛后看了官方题解,虽然各种orz但是依旧只能orz(标程写得真是犀利),然后可耻的到网上找了下题解... 做法是线段树 + 离线搞, 网上那种对于[l, r]中的l从大到小排序很精妙(有一篇竟然说是以r为关键字,,,然后代码里面却是l...汗), 再注意到gcd()对于每一个起点来说,是单调递减的,这样可以乱搞了... 如果还不是很明白