下面的题目都是更新点查找区间或者是更新区间查找点的题目,这一类属于比较简单的线段树的题目,可以用来入门或者加强对线段树的理解。
后面几道稍微有点难想到,但是想到怎么使用线段树之后就是简单的是用线段树了。
HDU 1754
http://acm.hdu.edu.cn/showproblem.php?pid=1754
简单的更新点的值,查找区间的最大值,很好写。
HDU 1394
http://acm.hdu.edu.cn/showproblem.php?pid=1394
简单的更新点的值,查找区间的和值,也很好写。
HDU 2795
http://acm.hdu.edu.cn/showproblem.php?pid=2795
查找区间内大于给定值的最小的下标,灵活的使用线段树求解区间最大值。这道题倒是可以看成是线段树的变形题了,感觉很好的题目。
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #define MAX 200020 #define lson l,m,k<<1 #define rson m+1,r,k<<1|1 using namespace std; int seg[MAX<<2]; int h,w,n; void Init(int l,int r,int k) { seg[k]=w; if(l==r) return ; int m = (l+r)>>1; Init(lson); Init(rson); } void PushUp(int rt) { seg[rt]=max(seg[rt<<1],seg[rt<<1|1]); } int query(int x,int l,int r,int k) { if (l == r) { seg[k] -= x; return l; } int m = (l + r) >> 1; int ret; if(seg[k<<1]>=x) ret = query(x,lson); else ret = query(x,rson); PushUp(k); return ret; } int main() { while(scanf("%d%d%d",&h,&w,&n)!=EOF) { if(h>n) h=n; Init(1,h,1); while(n--) { int t; scanf("%d",&t); if(seg[1]<t) printf("-1\n"); else printf("%d\n",query(t,1,h,1)); } } return 0; }
POJ 2828
http://poj.org/problem?id=2828
这道题也是线段树的灵活应用,但是前提是要先想到如何使用线段树解决这个问题。
我们来分析插队的情况,如果一个人插的位置是j,那么表示该人插的时候前面已经有了j个人;又由于插队,在前面有相同个数人的时候,是后插的人排在前面;因此我们从输入的序列的后面向前确定每一个人的位置。
比如a[i]=j,那么我们要找到最小的下标使得[1,x]这个区间的空位正好是j+1个,那么x就是这个人的正确的位置。
这样我们用线段树表示一个区间的空闲的位置数,也就是还没有被占领的位置,那么每次查找相应的需要的空位个数的最小的下标就是这个人所应该站的位置。
我想应该说的比较清楚了吧,还不清楚就看代码,是吧更新放在了Query中。
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> #define lson l,m,k<<1 #define rson m+1,r,k<<1|1 #define MAX 200020 using namespace std; typedef struct NODE { int p,v; }Node; Node node[MAX]; int seg[MAX<<2]; void PushUp(int k) { seg[k]=seg[k<<1]+seg[k<<1|1]; } void Init(int l,int r,int k) { if(l==r) { seg[k]=1; return; } int m = (l+r)>>1; Init(lson); Init(rson); PushUp(k); } int Query(int v,int l,int r,int k) { if(l==r) { seg[k]--; return l; } int m = (l+r)>>1; int res; if(seg[k<<1]>=v) res = Query(v,lson); else res = Query(v-seg[k<<1],rson); PushUp(k); return res; } bool cmp(Node a,Node b) { if(a.p<b.p) return true; else return false; } int main() { int n; while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) { scanf("%d%d",&node[i].p,&node[i].v); ++node[i].p; } Init(1,n,1); for(int i=n;i>=1;i--) { int t = Query(node[i].p,1,n,1); node[i].p=t; } sort(node+1,node+n+1,cmp); for(int i=1;i<=n;i++) printf("%d%c",node[i].v,i==n?'\n':' '); } return 0; }
POJ 2886 更新点查找区间的和值
http://poj.org/problem?id=2886
这道题的关键是在每次删除一个人之后快速找到该人的号码指定的人。
我们知道剩下的人的个数,然后从该人往前或者往后的处理,都有一个取模的过程,注意取模的时候为零的时候特殊处理,这样就行了。
还有就是快速求一个不大于n的因数个数最多的数,也就是反素数,使用的是别人的反素数表。为什么是对的也很容易理解,从当前的反素数到下一个反素数之间的所有数的约数都是小于等于这个反素数的约数个数的,所以反素数一定是一个范围内最早出现的约数个数最多的数。
#include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #define MAX 500020 #define lson l,m,k<<1 #define rson m+1,r,k<<1|1 using namespace std; typedef struct NODE { char name[12]; int t; }Node; Node node[MAX]; int seg[MAX<<2]; void PushUp(int k) { seg[k]=seg[k<<1]+seg[k<<1|1]; } int id; void Init(int l,int r,int k) { if(l==r) { if(l==id) seg[k]=0; else seg[k]=1; return ; } int m = (l+r)>>1; Init(lson); Init(rson); PushUp(k); } int Query1(int ll,int rr,int l,int r,int k) { if(ll>rr) return 0; if(ll==l&&rr==r) return seg[k]; int m = (l+r)>>1; if(rr<=m) return Query1(ll,rr,lson); else if(ll>m) return Query1(ll,rr,rson); else return Query1(ll,m,lson)+Query1(m+1,rr,rson); } int Query2(int v,int l,int r,int k) { if(l==r) { seg[k]--; return l; } int m = (l+r)>>1; int ans ; if(seg[k<<1]>=v) ans = Query2(v,lson); else ans = Query2(v-seg[k<<1],rson); PushUp(k); return ans; } int mod(int a,int b) { a %= b; a = (a+b)%b; if(a==0) return b; else return a; } const int antiprime[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840, 1260,1680,2520,5040,7560,10080,15120,20160,25200,27720, 45360,50400,55440,83160,110880,166320,221760,277200, 332640,498960,554400,665280};// 反质数表 const int factorNum[]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84, 90,96,100,108,120,128,144,160,168,180,192,200,216,224};// 对应的约数个数 int main() { int n,k; while(scanf("%d%d",&n,&k)!=EOF) { id = k; Init(1,n,1); for(int i=1;i<=n;i++) scanf("%s%d",node[i].name,&node[i].t); int left = n-1; int index = k; int ans=1,ansIndex=1; int cnt=0; while(cnt<35 && antiprime[cnt]<=n) cnt++; cnt--; for(int i=1;i<antiprime[cnt];i++) { if(node[index].t>0) { k = node[index].t+Query1(1,index-1,1,n,1); k = mod(k,left); index = Query2(k,1,n,1); } else { k = 1+node[index].t+Query1(1,index-1,1,n,1); k = mod(k,left); index = Query2(k,1,n,1); } left--; } printf("%s %d\n",node[index].name,factorNum[cnt]); } return 0; }
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------