HDU 3333 Turing Tree (主席树)

题意:给定上一个序列,然后有一些询问,求区间 l - r 中有多少个不同的数的和。

析:和求区间不同数的方法是一样,只要用主席树维护就好。

代码如下:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#include <sstream>
#include <list>
#define debug() puts("++++");
#define gcd(a, b) __gcd(a, b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 0x3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 30000 + 5;
const int maxm = maxn * 100;
const int mod = 10;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, 1, 0, -1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline bool is_in(int r, int c) {
  return r >= 0 && r < n && c >= 0 && c < m;
}

int a[maxn], T[maxn];
int lch[maxm], rch[maxm];
LL c[maxm];
int tot;
map<int, int> mp;

int build(int l, int r){
  int rt = tot++;
  c[rt] = 0;
  if(l == r)  return rt;
  int m = l + r >> 1;
  lch[rt] = build(l, m);
  rch[rt] = build(m+1, r);
  return rt;
}

int update(int rt, int pos, int val){
  int newrt = tot++;
  int tmp = newrt;
  c[newrt] = c[rt] + val;
  int l = 1, r = n;
  while(l < r){
    int m = l + r >> 1;
    if(pos <= m){
      lch[newrt] = tot++;
      rch[newrt] = rch[rt];
      newrt = lch[newrt];
      rt = lch[rt];
      r = m;
    }
    else {
      rch[newrt] = tot++;
      lch[newrt] = lch[rt];
      newrt = rch[newrt];
      rt = rch[rt];
      l = m + 1;
    }
    c[newrt] = c[rt] + val;
  }
  return tmp;
}

LL query(int rt, int pos){
  LL ans = 0;
  int l = 1, r = n;
  while(pos < r){
    int m = l + r >> 1;
    if(pos <= m){
      r = m;
      rt = lch[rt];
    }
    else{
      l = m + 1;
      ans += c[lch[rt]];
      rt = rch[rt];
    }
  }
  return ans + c[rt];
}

int main(){
  int t;  cin >> t;
  while(t--){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)  scanf("%d", a+i);
    mp.clear();
    tot = 0;
    T[n+1] = build(1, n);
    for(int i = n; i; --i){
      if(mp.count(a[i])){
        int tmp = update(T[i+1], mp[a[i]], -a[i]);
        T[i] = update(tmp, i, a[i]);
      }
      else  T[i] = update(T[i+1], i, a[i]);
      mp[a[i]] = i;
    }
    scanf("%d", &m);
    while(m--){
      int l, r;
      scanf("%d %d", &l, &r);
      printf("%I64d\n", query(T[l], r));
    }
  }
  return 0;
}

  

时间: 2024-10-08 01:38:39

HDU 3333 Turing Tree (主席树)的相关文章

hdu 3333 Turing Tree(线段树)

题目链接:hdu 3333 Turing Tree 题目大意:给定一个长度为N的序列,有M次查询,每次查询l,r之间元素的总和,相同元素只算一次. 解题思路:涨姿势了,线段树的一种题型,离线操作,将查询按照右区间排序,每次考虑一个询问,将mv ~ r的点全部标记为存在,并且对于每个位置i,如果A[i]在前面已经出现过了,那么将前面的那个位置减掉A[i],当前位置添加A[i],这样做维护了每个数尽量做,那么碰到查询,用sum[r] - sum[l-1]即可. #include <cstdio>

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

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

HDU 3333 Turing Tree(树状数组 || 线段树)

题意:给定一个区间,q个查询,对于每次查询回答这个区间内所有不重复的数的和. 思路:可以考虑使用树状数组来做. 先读入所有查询,离线来做,将所有查询按右端点升序排序. 那么我们从给定区间的第一个元素开始遍历这个区间,在此过程中更新每一个元素上一次出现的位置,每次将现在位置加上a[i]并将lastpos位置减去a[i], 也就是说,我们每一步都是保留与当前位置距离最近的重复元素值,其余置零,那么这样肯定能保证答案正确, 如果遍历到的这个位置恰好是一个询问的右端点,那么我们输出修改后的区间值之和.

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

题意: 给你一个数列,然后有n个查询,问你给定区间中不同数字的和是多少. 思路还是比较难想的,起码对于蒟蒻我来说. 将区间按照先右端点,后左端点从小到大排序之后,对于每个查询,我只要维护每个数字出现的最后一次就可以了(这个结论稍微想一下就可以证明是正确的). 然后就是简单的点更新,区间求和问题了- #include <cstdio> #include <cstring> #include <iostream> #include <map> #include

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

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(离线树状数组)

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

HDU 3333 Turing Tree (离线询问+线段树)

题目地址:HDU 3333 将询问离线保存下来,然后将数组的点离散化,记录每个值上一次出现的位置.然后枚举数组的数,若当前枚举的数前面出现过,那么就删掉前面出现过的那个位置上的数,更新当前这个位置上的数,然后那些所有询问的右端点为当前位置的就可以通过查询来得到结果了. 更新与查询用线段树来优化. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #

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