【暑假】[实用数据结构]动态范围查询问题

动态范围查询问题:

一、线段树+点修改

  支持操作:

  1. Update(x,v): 将Ax修改为v
  2. Query(L,R) : 计算[L,R]内的最小值
 1 int minv[maxn];
 2 int ql,qr;
 3 int Query(int u,int L,int R){
 4     int M=L + (R-L)/2 , ans=INF;
 5     if(ql<=L && R<=qr) return minv[u];
 6     if(ql <= M) ans=min(ans,Query(2*u,L,M));
 7     if(M < qr) ans=min(ans,Query(2*u+1,M+1,R));
 8     return ans;
 9 }
10
11 int p,v; //A[p]=v
12 void Update(int u,int L,int R){
13     if(L==R) {minv[u]=v; return; } //叶节点则修改
14     int M=L+(R-L)+1;
15     if(p<=M) Update(2*u,L,M); else Update(2*u+1,M+1,R); //递归p所在子树
16     minv[u]=min(minv[2*u],minv[2*u+1]); //更新当前minv
17 }

联系题目:LA3938

链接:

 二、线段树+区间修改

快速序列操作1:

支持操作:

  1. Add(L,R,v):
  2. Query(L,R):计算[L,R]内的最小值、最大值、区间和。

关于算法:在的基础上增加了addv,这种为了避免复杂操作而打标记的方法与链表题UVa12657相通,新增maintain维护结点信息。需要注意的是Query的新参数add是记录根到“叶子”路径上祖先的add之和。

 1 int minv[maxn],addv[maxn],sumv[maxn],maxv[maxn];
 2 //maintain维护u的结点信息
 3 void maintain(int u,int L,int R){
 4     int lc=2*u,rc=2*u+1;
 5     minv[u]=maxv[u]=sumv[u]=0;  //叶子结点的设置
 6     if(L<R){ //有子树
 7         minv[u]=min(minv[lc],minv[rc]);
 8         maxv[u]=max(maxv[lc],maxv[rc]);
 9         sumv[u]=sumv[lc]+sumv[rc];
10     }
11     minv[u] += addv[u]; maxv[u] += addv[u]; sumv[u] += addv[u]*(R-L+1);
12     //亦 叶子结点
13     //考虑到add
14 }
15
16
17 int y1,y2,v; //A[y1~y2] += v
18 void Add(int u,int L,int R){
19     if(y1<=L && R<=y2)  addv[u] +=v;
20     else{
21       int lc=2*u,rc=2*u+1;
22       int M=L+(R-L)+1;
23       if(y1 <= M) Add(lc,L,M);
24       if(M < y2) Add(rc,M+1,R);
25     }
26     maintain(u,L,R); //递归结束后维护u结点信息
27 }
28
29 int ql,qr;
30 int _min=INF,_max=-INF,_sum=0;
31 int Query(int u,int L,int R,int add){ //要考虑到祖先的add
32    if(ql<=L && R<=qr) {
33        _min=min(_min,minv[u]+add);
34        _max=max(_max,maxv[u]+add);
35        _sum += sumv[u]+ add*(R-L+1);
36    }
37   else{
38       int M=L+(R-L)/2;
39       if(ql <= M) Query(u*2,L,M,add+addv[u]); //add+addv[u]保证是根->叶该路径上的祖先add之和
40       if(M < qr) Query(u*2+1,M+1,R,add+addv[u]);
41   }
42 } 

快速序列操作2:

支持操作:

  1. Set(L,R,v) : A[L~R]=v
  2. Query(L,R): 计算[L,R]内的最小值、最大值、区间和。

关于算法:注意到 1 类题目的Add操作对于顺序是没有要求的,而 2 类题目中的Set则是有顺序要求,如果顺序改变结果亦会改变。所以泛泛地说:任意两个Set操作不能出现祖先后代的关系。所以必要时需要把Set下传给子结点。 但也可以找到set是祖先后辈关系的反例,这种情况是两操作并未“相遇”的结果,因为只有“相遇”才会pushdown。要知道处于上方的操作是下方操作之后标记的,于是加入递归边界1:有无set标记的判断。

int minv[maxn],setv[maxn],sumv[maxn],maxv[maxn];
//maintain维护u的结点信息
void maintain(int u,int L,int R){
    int lc=2*u,rc=2*u+1;
    minv[u]=maxv[u]=sumv[u]=0;  //叶子结点的设置
    if(L<R){ //有子树
        minv[u]=min(minv[lc],minv[rc]);
        maxv[u]=max(maxv[lc],maxv[rc]);
        sumv[u]=sumv[lc]+sumv[rc];
    }
    if(setv[u] >= 0) { minv[u]=maxv[u]=setv[u];sumv[u]=setv[u]*(R-L+1);}
}

void pushdown(int u){
    int lc=u*2,rc=u*2+1;
    if(setv[u]>=0){
        setv[lc]=setv[rc]=setv[u]; //下传 -1表示无set
        setv[u]=-1;
    }
}
int y1,y2,v; //A[y1~y2] += v
void Set(int u,int L,int R){
    if(y1<=L && R<=y2)  setv[u]=v;
    else{
      pushdown(u);  //下传set
      int lc=2*u,rc=2*u+1;
      int M=L+(R-L)+1;
      if(y1 <= M) Set(lc,L,M); else maintain(lc,L,M);
      if(M < y2) Set(rc,M+1,R); else maintain(rc,M+1,R);
      //针对不能递归的子树的两次maintain 维护信息
    }
    maintain(u,L,R); //递归结束后维护u结点信息
}

int ql,qr;
int _min=INF,_max=-INF,_sum=0;
int Query(int u,int L,int R){
    if(setv[u]>=0){            //递归边界1 有set标记  //注意sum范围
        _sum += setv[u] * (min(R,qr)-max(L,ql)+1);
        _min=min(_min,minv[u]);
        _max=max(_max,maxv[u]);
    }
    else if(ql<=L && R<=qr){ //递归边界2 边界区间 且 此区间没有受到set的影响
        _sum +=  sumv[u];
        _min=min(_min,minv[u]);
        _max=max(_max,maxv[u]);
    } else{
        int M=L+(R-L)/2;
        if(ql<=M) Query(u*2,L,M);
        if(M < qr) Query(u*2+1,M+1,R);
    }
} 

作者所给同时支持Add与Set操作的模板:

struct IntervalTree {
  int sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode];

  // 维护信息
  void maintain(int o, int L, int R) {
    int lc = o*2, rc = o*2+1;
    if(R > L) {
      sumv[o] = sumv[lc] + sumv[rc];
      minv[o] = min(minv[lc], minv[rc]);
      maxv[o] = max(maxv[lc], maxv[rc]);
    }
    if(setv[o] >= 0) { minv[o] = maxv[o] = setv[o]; sumv[o] = setv[o] * (R-L+1); }
    if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+1); }
  }

  // 标记传递
  void pushdown(int o) {
    int lc = o*2, rc = o*2+1;
    if(setv[o] >= 0) {
      setv[lc] = setv[rc] = setv[o];
      addv[lc] = addv[rc] = 0;
      setv[o] = -1; // 清除本结点标记
    }
    if(addv[o]) {
      addv[lc] += addv[o];
      addv[rc] += addv[o];
      addv[o] = 0; // 清除本结点标记
    }
  }

  void update(int o, int L, int R) {
    int lc = o*2, rc = o*2+1;
    if(y1 <= L && y2 >= R) { // 标记修改
      if(op == 1) addv[o] += v;
      else { setv[o] = v; addv[o] = 0; }
    } else {
      pushdown(o);
      int M = L + (R-L)/2;
      if(y1 <= M) update(lc, L, M); else maintain(lc, L, M);
      if(y2 > M) update(rc, M+1, R); else maintain(rc, M+1, R);
    }
    maintain(o, L, R);
  }

  void query(int o, int L, int R, int add) {
    if(setv[o] >= 0) {
      int v = setv[o] + add + addv[o];
      _sum += v * (min(R,y2)-max(L,y1)+1);
      _min = min(_min, v);
      _max = max(_max, v);
    } else if(y1 <= L && y2 >= R) {
      _sum += sumv[o] + add * (R-L+1);
      _min = min(_min, minv[o] + add);
      _max = max(_max, maxv[o] + add);
    } else {
      int M = L + (R-L)/2;
      if(y1 <= M) query(o*2, L, M, add + addv[o]);
      if(y2 > M) query(o*2+1, M+1, R, add + addv[o]);
    }
  }
};

联系题目:UVa11992

链接:

:如果代码中包含中文字符会乱码 ┑( ̄Д  ̄)┍

时间: 2024-08-03 07:30:50

【暑假】[实用数据结构]动态范围查询问题的相关文章

45个很实用的 Oracle 查询语句小结

?? 这里我们介绍的是 40+ 个很实用的 Oracle 查询语句,主要涵盖了日期操作,获取server信息.获取运行状态.计算数据库大小等等方面的查询. 这些是全部 Oracle 开发人员都必备的技能,所以快快收藏吧. 原文地址:http://www.jbxue.com/db/19890.html 日期/时间 相关查询 1.获取当前月份的第一天 执行这个命令能高速返回当前月份的第一天.你能够用不论什么的日期值替换 "SYSDATE"来指定查询的日期. 复制代码代码例如以下: SELE

【暑假】[实用数据结构]前缀树 Trie

前缀树Trie Trie可理解为一个能够快速插入与查询的集合,无论是插入还是查询所需时间都为O(m) 模板如下: 1 const int maxnode = 1000+10; 2 const int sigma_size = 26; 3 4 struct Trie{ 5 int ch[maxnode][sigma_size]; 6 int val[maxnode]; 7 int sz; 8 9 void clear(){ sz=1; memset(ch[0],0,sizeof(ch[0]));

【暑假】[实用数据结构]UVAlive

题目:   Dominating Patterns Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description The archaeologists are going to decipher a very mysterious ``language". Now, they know many language patterns; each patt

【暑假】[实用数据结构]UVAlive 3026 Period

UVAlive 3026 Period 题目: Period Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive),

【暑假】[实用数据结构]KMP

KMP算法 KMP算法是字符串匹配算法,可以在O(n)的时间完成,算法包含两部分,分别是:构造适配函数与两串匹配. 失配边的使用大大提高了算法效率,可以理解为已经成功匹配的字符不在重新匹配,因为我们已经知道它是什么,对应到算法中 匹配失败后应该在最大前缀之后继续匹配,因为某后缀已与最大前缀匹配成功而不用重新比较. 以下为代码实现: 1 const int maxn = 1000 + 5; 2 3 void getFail(char* P,int* f){ //构造失配边 4 int n=strl

【暑假】[实用数据结构]UVAlive 3942 Remember the Word

UVAlive 3942 Remember the Word 题目: Remember the Word Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description Neal is very curious about combinatorial problems, and now here comes a problem about words. Know

【暑假】[实用数据结构] AC自动机

Aho-Corasick自动机 AC自动机用于解决文本一个而模板有多个的问题. 作者所给模板如下: 1 struct AhoCorasickAutomata { 2 int ch[MAXNODE][SIGMA_SIZE]; 3 int f[MAXNODE]; // fail函数 4 int val[MAXNODE]; // 每个字符串的结尾结点都有一个非0的val 5 int last[MAXNODE]; // 输出链表的下一个结点 6 int cnt[MAXS]; 7 int sz; 8 9

【暑假】[实用数据结构]UVa11235 Frequent values

UVa 11235 Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 11241   Accepted: 4110 Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several qu

【暑假】[实用数据结构]UVa11991 Easy Problem from Rujia Liu?

UVa11991 Easy Problem from Rujia Liu?  思路:  构造数组data,使满足data[v][k]为第k个v的下标.因为不是每一个整数都会出现因此用到map,又因为每个数出现次数不等可能相差很大,因此用到vector. 注意:对于数据的清空与判空不要忘记,而map在调用之前必须有map.count的检查. 代码: 1 #include<cstdio> 2 #include<map> 3 #include<vector> 4 using