@morehigh
2017-02-25T03:10:11.000000Z
字数 6567
阅读 1157
线段树
【创建线段树(初始化)】:
在完全二叉树中假如一个结点的序号(数组下标)为 I ,那么 (二叉树基本关系)
I 的父亲为 I/2,
I 的另一个兄弟为 I/2*2 或 I/2*2+1
I 的两个孩子为 I*2 (左) I*2+1(右)
区间更新:
lazy操作
void pushdown(int num)
{
if(laz[num]!=0)
{
tre[num*2]+=laz[num];
tre[num*2+1]+=laz[num];
laz[num*2]+=laz[num];
laz[num*2+1]+=laz[num];
laz[num]=0;
}
}
必须在更新完儿子节点的值之后再反过来更新父亲节点。
A - 敌兵布阵
题意:
有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;(4)End 表示结束,这条命令在每组数据最后出现;每组数据最多有40000条命令
解题思路:
线段树,单点更新,维护区间和问题
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;struct Node{int a,b,sum;}t[150000];int r[50010],SUM;void make(int x,int y,int num){t[num].a=x;t[num].b=y;if(x==y)t[num].sum=r[y];else{make(x,(x+y)/2,num*2);make((x+y)/2+1,y,num*2+1);t[num].sum=t[2*num].sum+t[2*num+1].sum;}}void query(int x,int y,int num){if(x<=t[num].a&&y>=t[num].b)SUM+=t[num].sum;else{int adv=(t[num].a+t[num].b)/2;if(x>adv) query(x,y,num*2+1);else if(y<=adv) query(x,y,num*2);else{query(x,y,2*num);query(x,y,num*2+1);}}}void add(int x,int y,int num){t[num].sum+=y;if(t[num].a==x&&t[num].b==x)return;if(x>(t[num].a+t[num].b)/2)add(x,y,2*num+1);elseadd(x,y,2*num);}void sub(int x,int y,int num){t[num].sum-=y;if(t[num].a==x&&t[num].b==x)return;if(x>(t[num].a+t[num].b)/2)sub(x,y,2*num+1);elsesub(x,y,2*num);}int main(){int t,n;char com[10];cin>>t;int j=1;while(t--){int temp,a,b;scanf("%d",&n);r[0]=0;for(int i=1;i<=n;i++)scanf("%d",&r[i]);make(1,n,1);cout<<"Case "<<j++<<":"<<endl;while(cin>>com){if(strcmp(com,"End")==0)break;if(strcmp(com,"Query")==0){scanf("%d%d",&a,&b);SUM=0;query(a,b,1);cout<<SUM<<endl;}if(strcmp(com,"Add")==0){scanf("%d%d",&a,&b);add(a,b,1);}if(strcmp(com,"Sub")==0){scanf("%d%d",&a,&b);sub(a,b,1);}}}return 0;}
B - Color the ball
题意:
N个气球排成一排,接下来N行每行进行一次涂色操作,输入a,b,从气球a开始到气球b依次给每个气球涂一次颜色。求每个气球被涂了几次颜色?
解题思路:
每次操作更新区间涂色次数,最后将路径区间求和,得到涂色次数
代码:
树状数组代码#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int tree[500003];int lowbit(int x){return x&-x;}int update(int x,int val){while(x>0){tree[x]+=val;x-=lowbit(x);}}int query(int x,int n){int ans=0;while(x<=n){ans+=tree[x];x+=lowbit(x);}return ans;}int main(){int n,a,b;while(scanf("%d",&n)!=EOF&&n){memset(tree,0,sizeof(tree));for(int i=1;i<=n;i++){scanf("%d%d",&a,&b);update(b,1);update(a-1,-1);}for(int i=1;i<=n;i++){if(i!=n)cout<<query(i,n)<<" ";elsecout<<query(i,n)<<endl;}}return 0;}线段树代码#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;struct Node{int l,r,num;}tree[1000003];int ans[1000000];void build(int l,int r,int i){tree[i].l=l;tree[i].r=r;tree[i].num=0;if(l!=r){int mid=(l+r)/2;build(l,mid,2*i);build(mid+1,r,2*i+1);}}void query(int l,int r,int i){if(l==tree[i].l&&r==tree[i].r)tree[i].num+=1;else{int mid=(tree[i].l+tree[i].r)/2;if(r<=mid)query(l,r,2*i);else if(l>mid)query(l,r,2*i+1);else{query(l,mid,2*i);query(mid+1,r,2*i+1);}}}void solve(int x){for(int i=tree[x].l;i<=tree[x].r;i++)ans[i]+=tree[x].num;if(tree[x].l==tree[x].r)return;solve(2*x+1);solve(2*x);}int main(){int n,x,y;while(scanf("%d",&n)!=EOF&&n){build(1,n,1);for(int i=1;i<=n;i++){scanf("%d%d",&x,&y);query(x,y,1);}memset(ans,0,sizeof(ans));solve(1);for(int i=1;i<=n;i++){if(i!=n)cout<<ans[i]<<" ";elsecout<<ans[i]<<endl;}}return 0;}
D - Tunnel Warfare
题意:
n个村庄,m个事件,三种操作1.D x:第x个村庄被摧毁2.Q x:输出第x个村庄可以直接和间接相连的村庄个数3.R:最后一个被摧毁的村庄重建
解题思路:
对于一个点,看它是在当前区间的左半还是右半。在左半的话,看看是不是在右端的连续区间内,是的话,还要加上右半区间的左端连续区间。否则的话,只要计算它在左半区间的连接情况即可,在右半的话同理,看看是不是在左端的连续区间内,是的话,还要加上左半区间的右端连续区间。否则的话,只要计算它在右半区间的连接情况即可
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;struct Node{int l,r;int ls,rs,ms;}tree[150000];int op[50050];int n,m;void inittree(int l,int r,int i){tree[i].l=l;tree[i].r=r;tree[i].ls=tree[i].rs=tree[i].ms=r-l+1;if(l<r){int mid=(r+l)/2;inittree(l,mid,2*i);inittree(mid+1,r,2*i+1);}}void update(int x,int flag,int i){if(tree[i].l==tree[i].r){if(flag)tree[i].ls=tree[i].rs=tree[i].ms=1;elsetree[i].ls=tree[i].rs=tree[i].ms=0;return ;}int mid=(tree[i].l+tree[i].r)/2;if(x<=mid)update(x,flag,2*i);elseupdate(x,flag,2*i+1);if(tree[2*i].ls==tree[2*i].r-tree[2*i].l+1)tree[i].ls=tree[2*i].ls+tree[2*i+1].ls;elsetree[i].ls=tree[2*i].ls;if(tree[2*i+1].rs==tree[2*i+1].r-tree[2*i+1].l+1)tree[i].rs=tree[2*i].rs+tree[2*i+1].rs;elsetree[i].rs=tree[2*i+1].rs;tree[i].ms=max(max(tree[2*i].ms,tree[2*i+1].ms),tree[2*i].rs+tree[2*i+1].ls);}int search(int x,int i){if(tree[i].ms==0||tree[i].l==tree[i].r||tree[i].ms==tree[i].r-tree[i].l+1)return tree[i].ms;int mid=(tree[i].l+tree[i].r)/2;if(x<=mid){if(x>=tree[2*i].r-tree[2*i].rs+1)return tree[2*i].rs+tree[2*i+1].ls;elsereturn search(x,2*i);}else{if(x<=tree[2*i+1].l+tree[2*i+1].ls-1)return tree[2*i].rs+tree[2*i+1].ls;elsereturn search(x,2*i+1);}}int main(){char com[5];int x;while(scanf("%d%d",&n,&m)!=EOF){inittree(1,n,1);int top=0;for(int i=0;i<m;i++){scanf("%s",com);if(com[0]=='D'){scanf("%d",&x);op[++top]=x;update(x,0,1);}else if(com[0]=='Q'){scanf("%d",&x);cout<<search(x,1)<<endl;}else if(com[0]=='R'){if(top>0){x=op[top--];update(x,1,1);}}}}return 0;}
E - A Simple Problem with Integers
题意:
N个数字,进行Q次操作。(1 ≤ N,Q ≤ 100000) 两种操作方法:"C a b c",从第a到b个数字都加上c"Q a b",查询从a到b所有数字之和
解题思路:
线段树的区间更新问题。在区间更新的时候,为了节约时间,便利到左右儿子的区间在(x,y)之间的话,暂时不做修改,但是要把这个修改动作存在laz[num]里,下次需要的时候再修改。
代码:
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;#define ll long long#define CL(a,b) memset(a,b,sizeof(a))#define MAXN 100010struct node{int l,r;ll s,add;//add为每次加的数}t[MAXN*4];int hh[MAXN];int n,q;ll ans;void build(int l, int r, int i){t[i].l = l;t[i].r = r;t[i].add = 0;if(l == r) return ;int mid = (l+r)/2;build(l, mid, i*2);build(mid+1, r, i*2+1);t[i].s = t[i*2].s+t[i*2+1].s;}void update(int l, int r, int add, int i){if(t[i].l>r || t[i].r<l) return ;if(t[i].l>=l && t[i].r<=r){t[i].s += (t[i].r-t[i].l+1)*add;t[i].add += add;return ;}if(t[i].add){t[i*2].s += (t[i*2].r-t[i*2].l+1)*t[i].add;t[i*2].add += t[i].add;t[i*2+1].s += (t[i*2+1].r-t[i*2+1].l+1)*t[i].add;t[i*2+1].add += t[i].add;t[i].add = 0;}update(l, r, add, i<<1);update(l, r, add, i<<1|1);t[i].s = t[i*2].s+t[i*2+1].s;}void query(int l, int r, int i){if(t[i].l>r || t[i].r<l) return ;if(t[i].l>=l && t[i].r<=r){ans += t[i].s;return ;}if(t[i].add){t[i*2].s += (t[i*2].r-t[i*2].l+1)*t[i].add;t[i*2].add += t[i].add;t[i*2+1].s += (t[i*2+1].r-t[i*2+1].l+1)*t[i].add;t[i*2+1].add += t[i].add;t[i].add = 0;}query(l, r, i*2);query(l, r, i*2+1);t[i].s = t[i*2].s+t[i*2+1].s;}int main(){int a,b,c;ll k;char ch;while(scanf("%d%d",&n,&q)==2){for(int i=1; i<=n; i++)scanf("%d",&hh[i]);build(1, n, 1);for(int i=1; i<=n; i++)update(i, i, hh[i], 1);while(q--){getchar();scanf("%c",&ch);if(ch == 'C'){scanf("%d%d%d",&a,&b,&c);update(a, b, c, 1);}if(ch == 'Q'){ans = 0;scanf("%d%d",&a,&b);query(a, b, 1);cout<<ans<<endl;}}}return 0;}