HDU 3333 Turing Tree 树状数组 离线查询

题意: 给你一个数列,然后有n个查询,问你给定区间中不同数字的和是多少。

思路还是比较难想的,起码对于蒟蒻我来说。

将区间按照先右端点,后左端点从小到大排序之后,对于每个查询,我只要维护每个数字出现的最后一次就可以了(这个结论稍微想一下就可以证明是正确的)。

然后就是简单的点更新,区间求和问题了~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include <deque>
#include <bitset>
#include <list>
#include <cstdlib>
#include <climits>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <stack>
#include <sstream>
#include <numeric>
#include <fstream>
#include <functional>

using namespace std;

#define MP make_pair
#define PB push_back
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<int> VI;
typedef pair<int,int> pii;
const int INF = INT_MAX / 3;
const double eps = 1e-8;
const LL LINF = 1e17;
const double DINF = 1e60;
const int maxn = 3e4 + 10;

struct Seg {
    int l,r,id;
    LL ans;
    Seg(int l,int r,int id): l(l),r(r),id(id) {}
}; 

int n,val[maxn],last[maxn],m;
VI num;
vector<Seg> query;
LL C[maxn];
bool ext[maxn];

bool cmp(const Seg &a,const Seg &b) {
    if(a.r == b.r) return a.l < b.l;
    return a.r < b.r;
}

bool cmp1(const Seg &a,const Seg &b) {
    return a.id < b.id;
}

inline int lowbit(int x) {
    return x & (-x);
}

void addv(int pos,LL v) {
    while(pos <= n) {
        C[pos] += v; pos += lowbit(pos);
    }
}

LL ask_(int pos) {
    LL ret = 0;
    while(pos > 0) {
        ret += C[pos]; pos -= lowbit(pos);
    }
    return ret;
}

LL ask(int l,int r) {
    return ask_(r) - ask_(l - 1);
}

int getID(int Val) {
    return lower_bound(num.begin(),num.end(),Val) - num.begin() + 1;
}

void solve() {
    memset(C,0,sizeof(C));
    memset(last,-1,sizeof(last));
    int npos = 1;
    for(int i = 0;i < m;i++) {
        Seg &now = query[i];
        while(npos <= now.r) {
            int nowval = getID(val[npos]);
            if(last[nowval] != -1) addv(last[nowval],-val[npos]);
            last[nowval] = npos;
            addv(npos,val[npos]);
            npos++;
        }
        now.ans = ask(now.l,now.r);
    }
}

int main() {
    int T; scanf("%d",&T);
    while(T--) {
        query.clear();
        num.clear();
        int tmp; scanf("%d",&n);
        for(int i = 1;i <= n;i++) {
            scanf("%d",&val[i]); num.PB(val[i]);
        }
        sort(num.begin(),num.end());
        num.erase(unique(num.begin(),num.end()),num.end());
        scanf("%d",&m);
        for(int i = 0;i < m;i++) {
            int l,r; scanf("%d%d",&l,&r);
            query.PB(Seg(l,r,i));
        }
        sort(query.begin(),query.end(),cmp);
        solve();
        sort(query.begin(),query.end(),cmp1);
        for(int i = 0;i < m;i++) cout << query[i].ans << endl;
    }
    return 0;
}

  

HDU 3333 Turing Tree 树状数组 离线查询,布布扣,bubuko.com

时间: 2024-10-18 08:37:27

HDU 3333 Turing Tree 树状数组 离线查询的相关文章

hdu 3333 Turing Tree (树状数组+离线处理+离散化)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3981    Accepted Submission(s): 1349 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems a

HDU 3333 Turing Tree (树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3333 题意就是询问区间不同数字的和. 比较经典的树状数组应用. 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstrin

hdu 3333 Turing Tree(树状数组离线操作)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3904    Accepted Submission(s): 1325 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems

HDU Turing Tree --树状数组+离线处理

题意:统计一段序列[L,R]的和,重复元素只算一次. 解法:容易看出在线做很难处理重复的情况,干脆全部讲查询读进来,然后将查询根据右端点排个序,然后离散化数据以后就可以操作了. 每次读入一个数,如果这个数之前出现过,那么删除之前出现的那个数,改加上这个数,然后进行所有右端点小于等于此时下标的查询即可. 关于正确性,引用sdj222555的话来说,"观察一个区间,我们可以发现,如果出现重复的,尽量删除左边的,保留右边的,那么右端点相同的区间都可以进行查询." 代码: #include &

HDU 3333 | Codeforces 703D 树状数组、离散化

HDU 3333:http://blog.csdn.net/julyana_lin/article/details/7877164 这两个题是类似的,都是离线处理查询,对每次查询的区间的右端点进行排序.这里我们需要离散化处理一下,标记一下前面是否出现过这个值,然后不断更新last数组(该数组保存的是每个数最后一次出现的位置).最后用树状数组维护. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4

【树状数组+离线查询】HDU 3333 Turing Tree

https://www.bnuoj.com/v3/contest_show.php?cid=9149#problem/H [题意] 给定一个数组,查询任意区间内不同数字之和. (n<=30000,Q<=100000,每个数字<=1 000 000 000) [思路] 要算任意区间内不同数字之和,如果我们从左到右依次处理,每次只保留最右边出现的数字,处理以当前数字为右端点的查询,就能做到"不同数字之和",即不重不漏.因此我们要离线处理查询,按记录每个数作为右端点的所有查

(树状数组+离线查询)HDU 4417 - Super Mario

题意: 给定一个数列,最多10万次查询l到r不超过h的数字的个数. 分析: 唉,太菜啦. 在线做法应该比较明显,区间维护平衡树,用线段树套平衡树,或者分块套平衡树,应该都能A,但是没试过,只是BB,如有错误欢迎指正. 其实最方便的做法离线做法,太巧妙啦. 把数列按升序排列,把所有查询按h升序排列. 每次查询把比h的小的位置标记为1,查询用bit的sum(r)-sum(l-1)即可 因为都是单调的,所以很方便. 其实很多没有修改的区间问题都可以转化成离线问题. 甚至于一些带修改的题都可以离线. 这

HDU 3333 Turing Tree(树状数组离线处理)

HDU 3333 Turing Tree 题目链接 题意:给定一个数组,每次询问一个区间,求出这个区间不同数字的和 思路:树状数组离线处理,把询问按右端点判序,然后用一个map记录下每个数字最右出现的位置,因为一个数字在最右边出现,左边那些数字等于没用了,利用树状数组进行单点修改区间查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <map> using n

HDU 3333 Turing Tree (离散化+离线处理+树状数组)

Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick t