题目链接 hdu Color the ball
先是用单点更新做,发现竟然超时,因为很多节点多次,重复被访问,复杂度就有点高了
然后用lazy的思想,每次更新操作,并不更新到叶子节点,因为有些即使更新了,也不会被用到,所以只更新到当前区间,等到下一次有需要时,则继续往下更新
超时代码和ac代码都附上
单点更新(超时)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 111111
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int sum[maxn<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
sum[rt]=0;
if(l==r) return ;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int p,int l,int r,int rt)
{
if(l==r) return sum[rt];
int m=(l+r)>>1;
int ret=0;
if(p<=m) ret=query(p,lson);
else ret=query(p,rson);
return ret;
}
void update(int a,int b,int l,int r,int rt)
{
if(l==r)
{
sum[rt]++;
return ;
}
int m=(l+r)>>1;
if(a<=m) update(a,b,lson);
if(b>m) update(a,b,rson);
pushup(rt);
}
int main()
{
int a,b,n,h;
while(~scanf("%d",&n),n)
{
h=n;
build(1,n,1);
while(h--)
{
scanf("%d%d",&a,&b);
update(a,b,1,n,1);
}
for(int i=1;i<n;i++)
printf("%d ",query(i,1,n,1));
printf("%d\n",query(n,1,n,1));
}
}
成段更新(AC代码)
#include<iostream>
#include<cstdio>
#define maxn 111111
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
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]; //因为左儿子上次可能被涂色,这次是涂色以来第一次被访问,所以左儿子区间的col值加上被涂色col[rt]次
col[rt<<1|1]+=col[rt]; //右儿子同理
sum[rt<<1]+=col[rt]*(m-(m>>1)); //整个父区间被涂色,左儿子整个被涂色,(m-(m>>1))为左儿子的区间长度
sum[rt<<1|1]+=col[rt]*(m>>1); //右儿子也是整个区间被涂色,(m>>1)为右儿子区间的长度
col[rt]=0; //用过一次后重新置为0
}
}
void build(int l,int r,int rt)
{
sum[rt]=0; //每个节点的sum和col都进行初始化
col[rt]=0;
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int query(int p,int l,int r,int rt)
{
if(l==r) return sum[rt];
pushdown(rt,r-l+1); //每一都要向下更新。因为并不是所有的叶子节点都被更新过
int m=(l+r)>>1;
int ret =0 ;
if(p<=m) ret=query(p,lson); //查询的点在中点左边,继续往左儿子找
else ret=query(p,rson); //否则往右儿子找
return ret;
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R) //找到一个子区间,然后不向下更新了
{
sum[rt]+=r-l+1; //当前区间被涂色,所有节点的sum值要加一,即加上区间的长度
col[rt]+=c; //染色的次数加c
return ;
}
pushdown(rt,r-l+1); //向下更新
int m=(l+r)>>1;
if(L<=m) update(L,R,c,lson); //一部分区间在左边,往左儿子更新
if(R>m) update(L,R,c,rson); //若有一部分在右边,则往右边更新
pushup(rt); //向上更新
}
int main()
{
int a,b,n,h;
while(~scanf("%d",&n),n)
{
h=n;
build(1,n,1);
while(h--)
{
scanf("%d%d",&a,&b);
update(a,b,1,1,n,1);
}
for(int i=1;i<n;i++)
printf("%d ",query(i,1,n,1));
printf("%d\n",query(n,1,n,1));
}
return 0;
}