关于静态不修改元素的O(1)查询的RMQ (Range Minimum/Maximum Query)算法区间最大最小值参见我(Comzyh)另一篇文章
先考虑一道题:TYVJ1039 忠诚2 (看不了的本文末尾有题)
话说TYVJ总是喜欢出这么赤裸裸的算法题,我也喜欢。
很显然 m(m<=100000)笔账,n表示有n个问题,n<=100000 \( \Theta ( N^{2})\)的算法必然是不行的
话说ST(Sparse Table)算法不是查询O(1)么?ST算法的结构表明,ST的查询复杂度永远是O(1)的,但是动态修改基本上每次是O(NlogN)小点的,对于小更新量还可以,大量修改就不行了,事实证明,我写的动态ST算法超时3点。
这时,线段树的修改(Log N)优势就体现出来了。(因为本题更新的是点,比较特殊,是logN)
修改(Insert)时由顶而下以类似二分的方法找到最下面,然后回溯,比较刚更新的点与另一子树,求Min,更新为当前线段的Min值
这个模型比染色用的线段树简单多了,于是它成了我线段树AC第一题(话说在机房值日的感觉~~爽!)
贴之代码:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99  | 
						/* Problem: 1236		User: comzyh Memory: 240K		Time: 0MS Language: C++		Result: Accepted */ #include <cstdio> #include <cstring> #include <cstdlib> int tab[101][101]; int DFN[101],LOW[101],stack[200],instack[200]; int f[101]; int belong[101]; int top,RB,RT;//栈顶,最后一个强连通分量编号,最后一个时间戳  int ein[101],eout[101],ans,ans1,ans2; int N; //Function void Tarjan(int x); int getf(int x); inline void add(int x,int y);//add x to y int main() {     int i,j,to;     scanf("%d",&N);     memset(tab,0,sizeof(tab));     for (i=1;i<=N;i++)         while (scanf("%d",&to),to)               tab[i][++tab[i][0]]=to;     //     memset(DFN,0,sizeof(DFN));     memset(instack,0,sizeof(instack));     top=RB=RT=0;     for (i=1;i<=N;i++)         f[i]=i;     for (i=1;i<=N;i++)         if (!DFN[i])            Tarjan(i);     //for (i=1;i<=N;i++)printf("%4d belong to %4d\n",i,belong[i]);     memset(ein,0,sizeof(ein));     memset(eout,0,sizeof(eout));     for (i=1;i<=N;i++)         if (tab[i][0])         {            eout[belong[i]]=1;            for (j=1;j<=tab[i][0];j++)                 if (getf(i)!= getf(tab[i][j]))ein[belong[tab[i][j]]]=1;         }     ans1=ans2=0;     for (i=1;i<=RB;i++)     {         if (!ein[i] )ans1++;         if (!eout[i])ans2++;     }     ans=(ans1>ans2)?ans1:ans2;     if (RB==1)ans=0;//如果就一个分量,显然不用再连接      printf("%d\n%d",ans1,ans);     system("pause"); } void Tarjan(int x) {      int i,t;//t= top / to 多用途,       stack[++top]=x;      DFN[x]=LOW[x]=++RT;      instack[x]=1;      for (i=1;i<=tab[x][0];i++)      {          t=tab[x][i];          if (!DFN[t])          {             Tarjan(t);             if (LOW[t] < LOW[x])                LOW[x] = LOW[t];          }          else          if (instack[t] && DFN[t]<LOW[x])             LOW[x]=DFN[t];      }      if (LOW[x] == DFN[x])      {         RB++;         do         {              t=stack[top--];              add(t,x);              belong[t]=RB;              instack[t]=0;         }while(t != x);      } } int getf(int x) {     if (f[x] == x)return x;     else return f[x]=getf(f[x]); } inline void add(int x,int y)//add x to y {      int fx=getf(x),fy=getf(y);      if (fx == fy)return ;      f[fx]=fy; }  | 
					
题目在这里,点下箭头展开。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						描述 Description     老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。   在询问过程中账本的内容可能会被修改 输入格式 Input Format     输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。 接下来每行为3个数字,第一个p为数字1或数字2,第二个数为x,第三个数为y 当p=1 则查询x,y区间 当p=2 则改变第x个数为y 输出格式 Output Format  	输出文件中为每个问题的答案。具体查看样例。 样例输入 Sample Input 10 3 1 2 3 4 5 6 7 8 9 10 1 2 7 2 2 0 1 1 10 样例输出 Sample Output 2 0  | 
					
原创文章,转载请注明: 转载自Comzyh的博客
Fantastic stuff, I redesigned my site and the serps plummeted
动态RMQ问题(区间最值)的O(LogN)线段树解法-TYVJ1039忠诚2 – The newest addition to my weekly read