线段树之成段更新( 需要用到延迟标记,简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候)
链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698
线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int maxn=100001; int sum[maxn<<2]; int col[maxn<<2]; void pushUP(int rt) //当前节点信息更新给父节点 { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void pushdown(int rt,int m) //当前节点信息更新给儿子节点 { if(col[rt]) { col[rt<<1]=col[rt<<1|1]=col[rt]; sum[rt<<1]=(m-(m>>1))*col[rt]; sum[rt<<1|1]=(m>>1)*col[rt]; col[rt]=0; } } void build(int l,int r,int rt) //建立线段树 { col[rt]=0; sum[rt]=1; if(l==r) return; int m=(l+r)/2; build(lson); build(rson); pushUP(rt); } void update(int L,int R,int c,int l,int r,int rt) //成段替换,由于只query一次总区间,所以可以直接输出1节点的信息 { if(L<=l&&r<=R) { col[rt]=c; sum[rt]=(r-l+1)*c; return; } pushdown(rt,r-l+1); int m=(l+r)/2; if(L<=m) update(L,R,c,lson); if(R>m) update(L,R,c,rson); pushUP(rt); } int main() { int T,n,m; scanf("%d",&T); for(int cas=1;cas<=T;cas++) { scanf("%d%d",&n,&m); build(1,n,1);; while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); update(a,b,c,1,n,1); } printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]); } return 0; }</span>
poj 3468
链接:http://poj.org/problem?id=3468
题解:很好的一个区间更新的线段树的模型,线段树的功能,update成段增减,query区间求和,关键在于对pushdown的理解,当扩展区间与节点上区间完全吻合时,停止向下
#include<iostream> #include<cstdio> #include<cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int maxn=100001; long long sum[maxn<<2],col[maxn<<2]; void pushUP(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void pushdown(int rt,int m) { if(col[rt]) { col[rt<<1]+=col[rt]; col[rt<<1|1]+=col[rt]; sum[rt<<1]+=(m-(m>>1))*col[rt]; sum[rt<<1|1]+=(m>>1)*col[rt]; col[rt]=0; } } void build(int l,int r,int rt) { col[rt]=0; if(l==r) { scanf("%lld",&sum[rt]); return; } int m=(l+r)/2; build(lson); build(rson); pushUP(rt); } void update(int L,int R,int c,int l,int r,int rt) //成段增减 { if(L<=l&&r<=R) { col[rt]+=c; sum[rt]+=c*(r-l+1); return; } pushdown(rt,r-l+1); int m=(l+r)/2; if(L<=m) update(L,R,c,lson); if(R>m) update(L,R,c,rson); pushUP(rt); } long long query(int L,int R,int l,int r,int rt) //区间求和 { if(L<=l&&r<=R) return sum[rt]; pushdown(rt,r-l+1); long long ret=0; int m=(l+r)/2; if(L<=m) ret+=query(L,R,lson); if(R>m) ret+=query(L,R,rson); return ret; } int main() { int n,q; scanf("%d%d",&n,&q); build(1,n,1); while(q--) { int a,b,c; char op[2]; scanf("%s",op); if(op[0]=='Q') { scanf("%d%d",&a,&b); printf("%lld\n",query(a,b,1,n,1)); } else { scanf("%d%d%d",&a,&b,&c); update(a,b,c,1,n,1); } } return 0; }
poj2528(线段树+离散化)
题目链接:http://poj.org/problem?id=2528
题目解答:
离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:
有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。
现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9
然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9
对其升序排序,得2 3 4 6 8 9 10
然后建立映射
2 3 4 6 8 9 10
↓ ↓ ↓ ↓ ↓ ↓ ↓
1 2 3 4 5 6 7
那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。
离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。
由于此题数据较大,所以此题要用离散化
线段树功能:update:成段替换 query:简单hash
#include<iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 11111; bool hash[maxn]; int li[maxn] , ri[maxn]; int X[maxn*3]; int col[maxn<<4]; int cnt; void PushDown(int rt) //更新到儿子节点(此处为覆盖,所以用这种方法更新) { if (col[rt] != -1) { col[rt<<1] = col[rt<<1|1] = col[rt]; col[rt] = -1; } } void update(int L,int R,int c,int l,int r,int rt) //成段替换 { if (L <= l && r <= R) { col[rt] = c; return ; } PushDown(rt); int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (m < R) update(L , R , c , rson); } void query(int l,int r,int rt) //简单的hash,cnt统计离散化以后的长度 { if (col[rt] != -1) { if (!hash[col[rt]]) cnt ++; hash[ col[rt] ] = true; return ; } if (l == r) return ; int m = (l + r) >> 1; query(lson); query(rson); } int Bin(int key,int n,int X[]) //二分查找的过程 { int l = 0 , r = n - 1; while (l <= r) { int m = (l + r) >> 1; if (X[m] == key) return m; if (X[m] < key) l = m + 1; else r = m - 1; } return -1; } int main() { int T , n; scanf("%d",&T); while (T --) { scanf("%d",&n); int nn = 0; for (int i = 0 ; i < n ; i ++) //离散化的过程 { scanf("%d%d",&li[i] , &ri[i]); X[nn++] = li[i]; X[nn++] = ri[i]; } sort(X , X + nn); int m = 1; for (int i = 1 ; i < nn; i ++) { if (X[i] != X[i-1]) X[m ++] = X[i]; } for (int i = m - 1 ; i > 0 ; i --) { if (X[i] != X[i-1] + 1) X[m ++] = X[i-1] + 1; } sort(X , X + m); memset(col , -1 , sizeof(col)); for (int i = 0 ; i < n ; i ++) { int l = Bin(li[i], m, X); int r = Bin(ri[i], m, X); update(l, r, i, 0, m - 1, 1); } cnt = 0; memset(hash, false, sizeof(hash)); query(0, m - 1, 1); printf("%d\n",cnt); } return 0; }