@morehigh
2017-02-25T11:10:11.000000Z
字数 6567
阅读 1010
线段树
【创建线段树(初始化)】:
在完全二叉树中假如一个结点的序号(数组下标)为 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);
else
add(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);
else
sub(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)<<" ";
else
cout<<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]<<" ";
else
cout<<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;
else
tree[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);
else
update(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;
else
tree[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;
else
tree[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;
else
return 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;
else
return 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 100010
struct 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;
}