3.2 区间信息的维护与查询
例题7 la 4329 http://acm.hust.edu.cn/vjudge/problem/13895
每个人有能力值, 一场比赛需要3个人, 要求下标中间的人作为裁判 , 且中间的人的能力值 要在左右两个人之间 ,问共能进行多少场比赛.
树状数组统计某个人前面有多少比他小的, 前大, 后小 后大, 情况等于 前面小* 后面大 或者前面大*后面小.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 class One_Tree_Array { ///一维树状数组 12 static const int M=1e5+10; ///点的个数 13 typedef int typev; 14 typev a[M]; 15 int n; 16 public: 17 void init(int tn) { ///传入点数,点下标 1 开始 18 n=tn; 19 for(int i=0; i<=n; i++) a[i]=0; 20 } 21 int lowb(int t) { 22 return t&(-t); 23 } 24 void add(int i,typev v) { 25 for(; i<=n; a[i]+=v,i+=lowb(i)); 26 } 27 typev sum(int i) { 28 typev s=0; 29 for(; i>0; s+=a[i],i-=lowb(i)); 30 return s; 31 } 32 } tree; 33 int n; 34 int a[M]; 35 LL frontSmall[M]; 36 LL frontBig[M]; 37 LL backSmall[M]; 38 LL backBig[M]; 39 LL solve() { 40 int big=1e5; 41 tree.init(big); 42 for(int i=0;i<n;i++){ 43 frontSmall[i]=tree.sum(a[i]); 44 frontBig[i]=tree.sum(big)-tree.sum(a[i]); 45 tree.add(a[i],1); 46 } 47 tree.init(big); 48 for(int i=n-1;i>=0;i--){ 49 backSmall[i]=tree.sum(a[i]); 50 backBig[i]=tree.sum(big)-tree.sum(a[i]); 51 tree.add(a[i],1); 52 } 53 LL sum=0; 54 for(int i=0;i<n;i++){ 55 sum+=frontSmall[i]*backBig[i]; 56 sum+=frontBig[i]*backSmall[i]; 57 } 58 return sum; 59 } 60 int main() { 61 #ifdef txtout 62 freopen("in.txt","r",stdin); 63 freopen("out.txt","w",stdout); 64 #endif // txtout 65 int t; 66 while(~scanf("%d",&t)) { 67 while(t--) { 68 scanf("%d",&n); 69 for(int i=0; i<n; i++) { 70 scanf("%d",&a[i]); 71 } 72 printf("%lld\n",solve()); 73 } 74 } 75 return 0; 76 }
例题8 uva11235 http://acm.hust.edu.cn/vjudge/problem/23846
给一个不减序列,每次查询一个区间中,出现次数最多的数, 输出次数.
预处理,把相同的数分到同一组,记录每组的个数和起点的下标, 对于查询的下标, 可以二分出哪几组在这个区间内, 完全在区间内的可以直接通过rmq查出个数的最大值, 两端部分在区间内的, 可以直接计算.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 class RMQ { ///区间最值查询(ST)离线算法 init O(n*logn) query O(1) 12 typedef int typec; ///点权类型 13 static const int M=1e5+10; ///点的个数 14 int LOG[M]; 15 typec dpmax[M][20],dpmin[M][20]; 16 public: 17 RMQ() { 18 LOG[0]=-1; 19 for(int i=1; i<M; i++) { 20 LOG[i]=LOG[i>>1]+1; 21 } 22 } 23 void init(int n,typec a[]) { ///传入点的个数,下标 1 开始 24 for(int i=1; i<=n; i++) { 25 dpmax[i][0]=dpmin[i][0]=a[i]; 26 } 27 for(int j=1; j<=LOG[n]; j++) { 28 for(int i=1; i+(1<<j)-1<=n; i++) { 29 int k=i+(1<<(j-1)); 30 dpmax[i][j]=max(dpmax[i][j-1],dpmax[k][j-1]); 31 dpmin[i][j]=min(dpmin[i][j-1],dpmin[k][j-1]); 32 } 33 } 34 } 35 typec get(int a,int b,bool big) { ///传入 1 返回 max,传入 0 返回 min 36 int k=LOG[b-a+1]; 37 b=b-(1<<k)+1; 38 if(big) return max(dpmax[a][k],dpmax[b][k]); 39 return min(dpmin[a][k],dpmin[b][k]); 40 } 41 } rmq; 42 int n,m; 43 int a[M]; 44 int number[M]; 45 int id[M]; 46 struct Q { 47 int x,y,answer; 48 } q[M]; 49 int Binary(int value,int len){ 50 int L=1,R=len,result=1; 51 while(L<=R){ 52 int mid=(L+R)>>1; 53 if(id[mid]<=value){ 54 result=mid; 55 L=mid+1; 56 } 57 else{ 58 R=mid-1; 59 } 60 } 61 return result; 62 } 63 void solve() { 64 int last=0; 65 for(int i=1; i<=n; i++) { 66 if(last==0||a[i]!=a[id[last]]) { 67 last++; 68 id[last]=i; 69 number[last]=1; 70 continue; 71 } 72 number[last]++; 73 } 74 rmq.init(last,number); 75 for(int i=0;i<m;i++){ 76 q[i].answer=1; 77 if(q[i].x==q[i].y) continue; 78 int l=Binary(q[i].x,last); 79 int r=Binary(q[i].y,last); 80 if(l==r){ 81 q[i].answer=q[i].y-q[i].x+1; 82 continue; 83 } 84 if(id[l]<q[i].x){ 85 q[i].answer=max(q[i].answer,id[l+1]-q[i].x); 86 l++; 87 } 88 q[i].answer=max(q[i].answer,q[i].y-id[r]+1); 89 r--; 90 if(l<=r){ 91 q[i].answer=max(q[i].answer,rmq.get(l,r,1)); 92 } 93 } 94 } 95 int main() { 96 #ifdef txtout 97 freopen("in.txt","r",stdin); 98 freopen("out.txt","w",stdout); 99 #endif // txtout 100 while(~scanf("%d",&n),n) { 101 scanf("%d",&m); 102 for(int i=1; i<=n; i++) { 103 scanf("%d",&a[i]); 104 } 105 for(int i=0; i<m; i++) { 106 scanf("%d%d",&q[i].x,&q[i].y); 107 } 108 solve(); 109 for(int i=0; i<m; i++) { 110 printf("%d\n",q[i].answer); 111 } 112 } 113 return 0; 114 }
例题9 LA3938 http://acm.hust.edu.cn/vjudge/problem/22105
做了好几天,这个题值得记。
给一个整数序列,查询一个区间[L,R]中,和最大的区间的起点和终点是多少,既找到 [x,y],满足x,y在 LR内, 且Ax+。。。Ay的和是LR中所有子段和中最大的,如果有多个sum相同,取x小的,如果还有多个x相同 ,取y小的。
线段树每个节点存4个值,start,end,就是这个区间的答案。 设该节点管辖范围 [L,R], leftend是从L开始累加到leftend,和最大的位置,如果有多个和相同,leftend保存最小的那个位置。
rightstart是 从rightstart开始累加到R,和最大的位置,如果有多个,还是保存最小的那个位置。
叶子节点四个值都相同。 其他节点由左右儿子pushup得到。 方法,和最大的子段有3种可能,完全在左儿子,完全在右儿子,由左儿子rightstart加到右儿子leftend。
从L开始,leftend有两种情况 , 一种还是左儿子的leftend, 一种是L一直加到右儿子的leftend, rightstart的更新同理。
查询我一直错,错在如果查询的区间在左右儿子都有的时候,不是简单的取个大的,还要考虑中间接在一起的情况。
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=5e5+10; 11 typedef pair<int,int> pii; 12 LL sum[M]; 13 class SegmentTree{ 14 #define lrrt int L,int R,int rt 15 #define iall 1,n,1 16 #define imid int mid=(L+R)>>1 17 #define lson L,mid,rt<<1 18 #define rson mid+1,R,rt<<1|1 19 static const int MV=5e5+10; 20 struct T{ 21 int Start,End,leftEnd,rightStart; 22 }tree[MV<<2]; 23 int n; 24 pii getBest(pii a,pii b){ 25 LL sa=sum[a.second]-sum[a.first-1]; 26 LL sb=sum[b.second]-sum[b.first-1]; 27 if(sa>sb) return a; 28 if(sa<sb) return b; 29 if(a.first<b.first) return a; 30 if(a.first>b.first) return b; 31 if(a.second<b.second) return a; 32 return b; 33 } 34 T combine(int L,int R,T l,T r){ 35 pii a=make_pair(l.Start,l.End); 36 pii b=make_pair(r.Start,r.End); 37 pii c=make_pair(l.rightStart,r.leftEnd); 38 a=getBest(a,b); 39 a=getBest(a,c); 40 T result; 41 result.Start=a.first; 42 result.End=a.second; 43 a=make_pair(L,l.leftEnd); 44 b=make_pair(L,r.leftEnd); 45 result.leftEnd=getBest(a,b).second; 46 a=make_pair(l.rightStart,R); 47 b=make_pair(r.rightStart,R); 48 result.rightStart=getBest(a,b).first; 49 return result; 50 } 51 void pushup(lrrt){ 52 tree[rt]=combine(L,R,tree[rt<<1],tree[rt<<1|1]); 53 } 54 void build(lrrt){ 55 if(L==R){ 56 tree[rt].Start=L; 57 tree[rt].End=L; 58 tree[rt].leftEnd=L; 59 tree[rt].rightStart=L; 60 return ; 61 } 62 imid; 63 build(lson); 64 build(rson); 65 pushup(L,R,rt); 66 } 67 T query(int x,int y,lrrt){ 68 if(x<=L&&R<=y) return tree[rt]; 69 T a,b; 70 bool f1=false,f2=false; 71 imid; 72 if(mid>=x){ 73 a=query(x,y,lson); 74 f1=true; 75 } 76 if(mid<y){ 77 b=query(x,y,rson); 78 f2=true; 79 } 80 if(!f1) return b; 81 if(!f2) return a; 82 return combine(x,y,a,b); 83 } 84 public: 85 void init(int tn){ 86 n=tn; 87 build(iall); 88 } 89 pii query(int x,int y){ 90 T result=query(x,y,iall); 91 return make_pair(result.Start,result.End); 92 } 93 }st; 94 int n,m; 95 int a[M]; 96 struct Q{ 97 int x,y; 98 pii answer; 99 }q[M]; 100 void init_sum(){ 101 sum[0]=0; 102 for(int i=1;i<=n;i++){ 103 sum[i]=sum[i-1]+a[i]; 104 } 105 } 106 void solve(){ 107 init_sum(); 108 st.init(n); 109 for(int i=0;i<m;i++){ 110 q[i].answer=st.query(q[i].x,q[i].y); 111 } 112 } 113 int main(){ 114 #ifdef txtout 115 freopen("in.txt","r",stdin); 116 freopen("out.txt","w",stdout); 117 #endif // txtout 118 int cas=1; 119 while(~scanf("%d%d",&n,&m)){ 120 for(int i=1;i<=n;i++){ 121 scanf("%d",&a[i]); 122 } 123 for(int i=0;i<m;i++){ 124 scanf("%d%d",&q[i].x,&q[i].y); 125 } 126 solve(); 127 printf("Case %d:\n",cas++); 128 for(int i=0;i<m;i++){ 129 printf("%d %d\n",q[i].answer.first,q[i].answer.second); 130 } 131 } 132 return 0; 133 }
例题10 uva11992 http://acm.hust.edu.cn/vjudge/problem/18697
对r*c的矩阵执行3个操作,子矩阵每个元素+v , 子矩阵每个元素赋值 v。 查询一个子矩阵元素和, 最大值,最小值。
由于最多20行,可以每行一个一维线段树。
线段树多两个标记,一个标记区间+,一个标记区间赋值。更新的时候,赋值操作会清零+操作的累计值。
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=2e4+10; 11 class SegmentTree{ 12 #define lrrt int L,int R,int rt 13 #define iall 1,n,1 14 #define imid int mid=(L+R)>>1 15 #define lson L,mid,rt<<1 16 #define rson mid+1,R,rt<<1|1 17 static const int MV=1e6+10; 18 struct T{ 19 int small,big,sum,lazy,cover; 20 }tree[MV<<2]; 21 int n; 22 void build(lrrt){ 23 tree[rt].small=0; 24 tree[rt].big=0; 25 tree[rt].sum=0; 26 tree[rt].lazy=0; 27 tree[rt].cover=0; 28 if(L==R) return ; 29 imid; 30 build(lson); 31 build(rson); 32 } 33 void pushup(int rt){ 34 tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; 35 tree[rt].big=max(tree[rt<<1].big,tree[rt<<1|1].big); 36 tree[rt].small=min(tree[rt<<1].small,tree[rt<<1|1].small); 37 } 38 void pushdown(int mid,lrrt){ 39 if(tree[rt].cover){ 40 tree[rt<<1].cover=tree[rt].cover; 41 tree[rt<<1|1].cover=tree[rt].cover; 42 tree[rt<<1].big=tree[rt].cover; 43 tree[rt<<1|1].big=tree[rt].cover; 44 tree[rt<<1].small=tree[rt].cover; 45 tree[rt<<1|1].small=tree[rt].cover; 46 tree[rt<<1].sum=tree[rt].cover*(mid-L+1); 47 tree[rt<<1|1].sum=tree[rt].cover*(R-mid); 48 tree[rt<<1].lazy=0; 49 tree[rt<<1|1].lazy=0; 50 tree[rt].cover=0; 51 } 52 if(tree[rt].lazy){ 53 tree[rt<<1].big+=tree[rt].lazy; 54 tree[rt<<1|1].big+=tree[rt].lazy; 55 tree[rt<<1].small+=tree[rt].lazy; 56 tree[rt<<1|1].small+=tree[rt].lazy; 57 tree[rt<<1].sum+=tree[rt].lazy*(mid-L+1); 58 tree[rt<<1|1].sum+=tree[rt].lazy*(R-mid); 59 tree[rt<<1].lazy+=tree[rt].lazy; 60 tree[rt<<1|1].lazy+=tree[rt].lazy; 61 tree[rt].lazy=0; 62 } 63 } 64 void update(int x,int y,int z,lrrt){ 65 if(x<=L&&R<=y){ 66 tree[rt].big+=z; 67 tree[rt].small+=z; 68 tree[rt].sum+=(R-L+1)*z; 69 tree[rt].lazy+=z; 70 return ; 71 } 72 imid; 73 pushdown(mid,L,R,rt); 74 if(mid>=x) update(x,y,z,lson); 75 if(mid<y) update(x,y,z,rson); 76 pushup(rt); 77 } 78 void change(int x,int y,int z,lrrt){ 79 if(x<=L&&R<=y){ 80 tree[rt].big=z; 81 tree[rt].small=z; 82 tree[rt].sum=(R-L+1)*z; 83 tree[rt].lazy=0; 84 tree[rt].cover=z; 85 return ; 86 } 87 imid; 88 pushdown(mid,L,R,rt); 89 if(mid>=x) change(x,y,z,lson); 90 if(mid<y) change(x,y,z,rson); 91 pushup(rt); 92 } 93 int querySum(int x,int y,lrrt){ 94 if(x<=L&&R<=y) return tree[rt].sum; 95 imid; 96 pushdown(mid,L,R,rt); 97 int sum=0; 98 if(mid>=x) sum+=querySum(x,y,lson); 99 if(mid<y) sum+=querySum(x,y,rson); 100 return sum; 101 } 102 int queryMax(int x,int y,lrrt){ 103 if(x<=L&&R<=y) return tree[rt].big; 104 imid; 105 pushdown(mid,L,R,rt); 106 int big=-1; 107 if(mid>=x) big=max(big,queryMax(x,y,lson)); 108 if(mid<y) big=max(big,queryMax(x,y,rson)); 109 return big; 110 } 111 int queryMin(int x,int y,lrrt){ 112 if(x<=L&&R<=y) return tree[rt].small; 113 imid; 114 pushdown(mid,L,R,rt); 115 int small=inf; 116 if(mid>=x) small=min(small,queryMin(x,y,lson)); 117 if(mid<y) small=min(small,queryMin(x,y,rson)); 118 return small; 119 } 120 public: 121 void init(int tn){ 122 n=tn; 123 build(iall); 124 } 125 void update(int x,int y,int z){ 126 update(x,y,z,iall); 127 } 128 void change(int x,int y,int z){ 129 change(x,y,z,iall); 130 } 131 int querySum(int x,int y){ 132 return querySum(x,y,iall); 133 } 134 int queryMax(int x,int y){ 135 return queryMax(x,y,iall); 136 } 137 int queryMin(int x,int y){ 138 return queryMin(x,y,iall); 139 } 140 }st[21]; 141 int r,c,m; 142 struct Q{ 143 int type,x1,y1,x2,y2,v,sum,big,small; 144 }q[M]; 145 void init(){ 146 for(int i=1;i<=r;i++){ 147 st[i].init(c); 148 } 149 } 150 void solve(){ 151 init(); 152 for(int i=0;i<m;i++){ 153 q[i].sum=0; 154 q[i].big=-1; 155 q[i].small=inf; 156 for(int j=q[i].x1;j<=q[i].x2;j++){ 157 if(q[i].type==1){ 158 st[j].update(q[i].y1,q[i].y2,q[i].v); 159 } 160 else if(q[i].type==2){ 161 st[j].change(q[i].y1,q[i].y2,q[i].v); 162 } 163 else{ 164 q[i].sum+=st[j].querySum(q[i].y1,q[i].y2); 165 q[i].big=max(q[i].big,st[j].queryMax(q[i].y1,q[i].y2)); 166 q[i].small=min(q[i].small,st[j].queryMin(q[i].y1,q[i].y2)); 167 } 168 } 169 } 170 } 171 int main(){ 172 #ifdef txtout 173 freopen("in.txt","r",stdin); 174 freopen("out.txt","w",stdout); 175 #endif // txtout 176 while(~scanf("%d%d%d",&r,&c,&m)){ 177 for(int i=0;i<m;i++){ 178 scanf("%d%d%d%d%d",&q[i].type,&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2); 179 if(q[i].type<3){ 180 scanf("%d",&q[i].v); 181 } 182 } 183 solve(); 184 for(int i=0;i<m;i++){ 185 if(q[i].type!=3) continue; 186 printf("%d %d %d\n",q[i].sum,q[i].small,q[i].big); 187 } 188 } 189 return 0; 190 }
end