@sensitive-cs
        
        2017-03-30T12:33:10.000000Z
        字数 8596
        阅读 1045
    题解
HDU 1166:敌兵布阵 
题意: 
给出n个数,有3种操作,一种是对某个数加上一个数,一种是对某个数减去一个数,一种是查询一个区间内的数的和。 
思路: 
线段树裸题,主要是点更新,也可以用树状数组做。(巨坑的是位运算的时候以为除以2,就写的2,导致wa了几发,引以为戒。 
代码:
#include <stdio.h>#include <string.h>int a[50005];int sum[200005];void pushup(int rt){sum[rt] = sum[rt<<1] + sum[rt << 1|1];}void build(int l,int r,int rt){if (l == r){sum[rt] = a[l];return;}int m = (l+r) >> 1;build(l,m,rt << 1);build(m+1,r,rt << 1|1);pushup(rt);}void update(int ll,int c,int l,int r,int rt){if (l == r){sum[rt] += c;return;}int m = (l+r) >> 1;if (ll <= m) update(ll,c,l,m,rt << 1);else update(ll,c,m+1,r,rt << 1|1);pushup(rt);}int query(int ll,int rr,int l,int r,int rt){if (ll <= l && r <= rr){return sum[rt];}int m = (l+r) >> 1;int ans = 0;if (ll <= m) ans += query(ll,rr,l,m,rt << 1);if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);return ans;}int main(){int t;scanf("%d",&t);for (int o = 1;o <= t;o++){int n;scanf("%d",&n);for (int i = 1;i <= n;i++)scanf("%d",&a[i]);build(1,n,1);char ch[20];printf("Case %d:\n",o);while (scanf("%s",ch) != EOF){if (ch[0] == 'E') break;int x,y;scanf("%d%d",&x,&y);if (ch[0] == 'S'){update(x,-y,1,n,1);}if (ch[0] == 'A'){update(x,y,1,n,1);}if (ch[0] == 'Q'){int ans = 0;ans = query(x,y,1,n,1);printf("%d\n",ans);}}}return 0;}
poj 3468:A SIMPLE PROBLEM WITH INTEGERS 
题意: 
给出n个数字,两种操作,一种是给给定区间内的每一个数加上一个数,另一种是查询某个区间内的数字之和。 
思路: 
纯线段树裸题,直接套模板。不过要注意先建树(又差点卡死2333,再进行操作。 
代码:
#include <string.h>#include <stdio.h>int a[100005];long long add[400005];long long sum[400005];void pushup(int rt){sum[rt] = sum[rt << 1] + sum[rt << 1|1];}void build(int l,int r,int rt){if (l == r){sum[rt] = a[l];return;}int m = (l+r) >> 1;build(l,m,rt << 1);build(m+1,r,rt << 1|1);pushup(rt);}void pushdown(int rt,int ln,int rn){if (add[rt]){add[rt << 1] += add[rt];add[rt << 1|1] += add[rt];sum[rt << 1] += ln * add[rt];sum[rt << 1|1] += rn *add[rt];add[rt] = 0;}}void update(int ll,int rr,int c,int l,int r,int rt){if (ll <= l && r <= rr){sum[rt] += c*(r-l+1);add[rt] += c;return;}int m = (l+r) >> 1;pushdown(rt,m-l+1,r-m);if (ll <= m) update(ll,rr,c,l,m,rt << 1);if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);pushup(rt);}long long query(int ll,int rr,int l,int r,int rt){if (ll <= l && r <= rr){return sum[rt];}int m = (l+r) >> 1;pushdown(rt,m-l+1,r-m);long long ans = 0;if (ll <= m) ans += query(ll,rr,l,m,rt << 1);if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);return ans;}int main(){int n,q;scanf("%d%d",&n,&q);memset(a,0,sizeof(a));memset(add,0,sizeof(add));memset(sum,0,sizeof(sum));for (int i = 1;i <= n;i++)scanf("%d",&a[i]);build(1,n,1);for (int i = 1;i <= q;i++){char s[10];scanf("%s",s);int x,y,z;if (s[0] == 'Q'){scanf("%d%d",&x,&y);long long res = query(x,y,1,n,1);printf("%I64d\n",res);}else{scanf("%d%d%d",&x,&y,&z);update(x,y,z,1,n,1);}}return 0;}
HDU 1556:COLOR THE BALL 
题意: 
给出n个区间,每次对1个区间进行更新,对区间的每个数都加1,问每个数被更新了多少次。 
思路: 
线段树裸题,区间更新,单点查询,也可以用区间查询,左右端点相同就可以了。 
代码:
#include <string.h>#include <stdio.h>int a[100005];int add[400005];int sum[400005];void pushup(int rt){sum[rt] = sum[rt << 1] + sum[rt << 1|1];}void build(int l,int r,int rt){if (l == r){sum[rt] = a[l];return;}int m = (l+r) >> 1;build(l,m,rt << 1);build(m+1,r,rt << 1|1);pushup(rt);}void pushdown(int rt,int ln,int rn){if (add[rt]){add[rt << 1] += add[rt];add[rt << 1|1] += add[rt];sum[rt << 1] += ln * add[rt];sum[rt << 1|1] += rn *add[rt];add[rt] = 0;}}void update(int ll,int rr,int c,int l,int r,int rt){if (ll <= l && r <= rr){sum[rt] += c*(r-l+1);add[rt] += c;return;}int m = (l+r) >> 1;pushdown(rt,m-l+1,r-m);if (ll <= m) update(ll,rr,c,l,m,rt << 1);if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);pushup(rt);}long long query(int ll,int rr,int l,int r,int rt){if (ll <= l && r <= rr){return sum[rt];}int m = (l+r) >> 1;pushdown(rt,m-l+1,r-m);long long ans = 0;if (ll <= m) ans += query(ll,rr,l,m,rt << 1);if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);return ans;}int main(){int n;while (scanf("%d",&n) == 1&& n){memset(a,0,sizeof(a));memset(add,0,sizeof(add));memset(sum,0,sizeof(sum));build(1,n,1);for (int i = 1;i <= n;i++){int a,b;scanf("%d%d",&a,&b);update(a,b,1,1,n,1);}for (int i = 1;i <= n;i++){int res = query(i,i,1,n,1);if (i == 1) printf("%d",res);else printf(" %d",res);}printf("\n");}return 0;}
HDU1540:tunnel warfare 
题意: 
有n个村庄,他们是连着的,由于战争,有些村庄被毁了,被毁了之后又恢复。现在有3种指令,一种是毁灭村庄,另一种是回复村庄,还有一种是查询一个村庄连着的村庄有多少个。 
思路1: 
由于是跟区间有关,求区间的最大长度,所以说用线段树的区间合并,注释在代码中。 
思路2: 
看了一篇博客,博主天纵奇才,想到只用线段树的单点更新。 
比如现在有1 2 3 4 5 6 7 8有8个村庄,我回去2和7,查询4的时候就是7-2-1。 
博主想到最大值树和最小值树,最大值树就用一个很大的之来初始化,最小值数就用-1初始化,每次毁掉村庄,就用这个村庄的端点去覆盖-1或者最大值。当查询的时候,就用查询点左边的最大值,和查询点右边的最小值,进行如7-2-1这样的操作就行了。 
注意防止越界的方法,把0点赋值为-1,n+1点赋值为无穷大就行了。 
代码1:
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;const int cnt = 50010;struct ss{int l,r;int ls,rs,ms;}node[4*cnt];int st[cnt];void build(int l,int r,int rt){node[rt].l = l,node[rt].r = r;node[rt].ls = node[rt].rs = node[rt].ms = r - l + 1;if (l != r){int mid = (l+r) >> 1;build(l,mid,rt << 1);build(mid+1,r,rt << 1|1);}return;}void update(int rt,int x,int flag){if (node[rt].l == node[rt].r){node[rt].ls = node[rt].ms = node[rt].rs = flag;return;}int mid = (node[rt].l + node[rt].r) >> 1;if (x <= mid) update(rt << 1,x,flag);else update(rt << 1|1,x,flag);node[rt].ls = node[rt << 1].ls;node[rt].rs = node[rt << 1|1].rs;if (node[rt << 1].ms == node[rt << 1].r - node[rt << 1].l + 1)node[rt].ls += node[rt << 1|1].ls;if (node[rt << 1|1].ms == node[rt << 1|1].r - node[rt << 1|1].l+1)node[rt].rs += node[rt << 1].rs;node[rt].ms = max(max(node[rt<<1].ms,node[rt << 1|1].ms),node[rt << 1].rs + node[rt << 1|1].ls);}int query(int rt,int x){if (node[rt].r == node[rt].l || node[rt].ms == 0 || node[rt].ms == node[rt].r - node[rt].l + 1){return node[rt].ms;}int mid = (node[rt].l + node[rt].r) >> 1;if (x <= mid){if (x < node[rt << 1].r - node[rt << 1].rs + 1)return query(rt << 1,x);elsereturn node[rt << 1].rs + node[rt << 1|1].ls;}else{if (x > node[rt << 1|1].l + node[rt << 1|1].ls - 1)return query(rt << 1|1,x);elsereturn node[rt << 1].rs + node[rt << 1|1].ls;}}int main(){int n,m;while (scanf("%d%d",&n,&m) != EOF){memset(st,0,sizeof(st));build(1,n,1);int top = 0;for (int i = 0;i < m;i++){char t[5];scanf("%s",t);if (t[0] == 'D'){int x;scanf("%d",&x);update(1,x,0);st[top++] = x;}if (t[0] == 'R'){int x = st[--top];update(1,x,1);}if (t[0] == 'Q'){int x;scanf("%d",&x);int sum = query(1,x);printf("%d\n",sum);}}}return 0;}
代码2
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;const int inf = 0x3f3f3f3f;const int cnt = 5e4+10;int ma[cnt << 2];int mi[cnt << 2];int st[cnt];int g = inf,v = -1;int m,n;void update(int rt,int x,int flag,int l,int r){if (l == r){if (flag){ma[rt] = -1;mi[rt] = inf;}elsemi[rt] = ma[rt] = x;return;}int mid = (l+r) >> 1;if (x <= mid) update(rt << 1,x,flag,l,mid);else update(rt << 1|1,x,flag,mid+1,r);ma[rt] = max(ma[rt << 1],ma[rt << 1|1]);mi[rt] = min(mi[rt << 1],mi[rt << 1|1]);return;}void query1(int rt,int l,int r,int L,int R){if (l >= L && r <= R){g = min(g,mi[rt]);return;}int mid = (l+r) >> 1;if (L <= mid) query1(rt << 1,l,mid,L,R);if (R > mid) query1(rt << 1|1,mid+1,r,L,R);}void query2(int rt,int l,int r,int L,int R){if (l >= L && r <= R){v = max(v,ma[rt]);return;}int mid = (l+r) >> 1;if (L <= mid) query2(rt << 1,l,mid,L,R);if (R > mid) query2(rt << 1|1,mid+1,r,L,R);}int main(){while (scanf("%d%d",&n,&m) != EOF){memset(ma,-1,sizeof(ma));memset(mi,inf,sizeof(mi));memset(st,0,sizeof(st));int top = 0;update(1,0,0,0,n+1);update(1,n+1,0,0,n+1);for (int i = 0;i < m;i++){char s[5];scanf("%s",s);if (s[0] == 'D'){int x;scanf("%d",&x);st[top++] = x;update(1,x,0,0,n+1);}if (s[0] == 'R'){int x = st[--top];update(1,x,1,0,n+1);}if (s[0] == 'Q'){int x;scanf("%d",&x);g = inf;v = -1;query1(1,0,n+1,x,n+1);query2(1,0,n+1,0,x);if (g == v)printf("0\n");elseprintf("%d\n",g - v - 1);}}}return 0;}
poj2528:mayor's posters 
题意: 
相当于区间染色问题,有一面墙,开始时没有颜色,给出n个操作,每一次染一种颜色,每次染的颜色都不同,问最后墙上有多少颜色。 
思路: 
一开始想到了是线段树,但是奈何区间太大,1000万的数量级。后来搜题解报告知道了是离散化,所以就浅浅的学习了一下。离散化,对于这题,就是把大的区间映射到小的区间以减小复杂度。但是这题的离散化比较特殊,需要在不连续的坐标之间随意添加一个中间小的数,才能够保证不出错,这一点可以自己举例子理解。最开始写线段树,忘了,就偷懒写了简化版的线段树,果断tle,后来再次按照模板写,wa了,再一看,居然是pushdown在query的时候也需要下方,唉,还是不要懒,more haste,less speed。谨记!!!
代码:
#include <stdio.h>#include <string.h>#include <map>#include <vector>#include <algorithm>using namespace std;bool cx[10000005];vector<int> xl;int x[10005],y[10005];int a[10000005];int ans[10005];int b[200005],lazy[200005];void pushdown(int rt){if (lazy[rt] != -1){lazy[rt << 1] = lazy[rt << 1|1] = lazy[rt];b[rt << 1] = b[rt << 1|1] = b[rt] = lazy[rt];lazy[rt] = -1;}}void update(int l,int r,int k,int ll,int rr,int rt){//printf("***\n");if (l >= ll && r <= rr){b[rt] = k;lazy[rt] = k;return;}else{pushdown(rt);int mid = (l + r) >> 1;if (ll <= mid) update(l,mid,k,ll,rr,rt << 1);if (rr > mid) update(mid+1,r,k,ll,rr,rt << 1|1);}}void query(int l,int r,int rt){if (l == r){ans[b[rt]]++;return;}pushdown(rt);int mid = (l+r) >> 1;query(l,mid,rt << 1);query(mid+1,r,rt << 1|1);}int main(){int t;scanf("%d",&t);while (t--){int n;memset(x,0,sizeof(x));memset(y,0,sizeof(y));memset(a,0,sizeof(a));memset(ans,0,sizeof(ans));memset(cx,0,sizeof(cx));memset(b,0,sizeof(b));memset(lazy,-1,sizeof(lazy));scanf("%d",&n);xl.clear();for (int i = 1;i <= n;i++){scanf("%d%d",&x[i],&y[i]);if (!cx[x[i]]){cx[x[i]] = 1;xl.push_back(x[i]);}if (!cx[y[i]]){cx[y[i]] = 1;xl.push_back(y[i]);}}sort(xl.begin(),xl.end());int len = xl.size();for (int i = 1;i < len;++i){if (xl[i] - xl[i-1] > 1)xl.push_back(xl[i] - 1);}sort(xl.begin(),xl.end());len = xl.size();for (int i = 0;i < len;i++){a[xl[i]] = i+1;}for (int i = 1;i <= n;i++){x[i] = a[x[i]];y[i] = a[y[i]];}for (int i = 1;i <= n;i++){update(1,len,i,x[i],y[i],1);}int sum = 0;query(1,len,1);for (int i = 1;i <= n;i++){if (ans[i]) sum++;}printf("%d\n",sum);}return 0;}