UVA 12436 Rip Van Winkle’s Code 线段树

http://www.caiyiwen.tech/article/25.html

  • Rip Van Winkle’s Code
    • 线段树维护等差数列,考察建模能力和多重懒标记优先级顺序。
    • 首先,我们需要看出题中所给的暴力代码实际上就是维护连续区间的等差数列加和。
    • 思考等差数列是否具有可加性:两个等差数列相加,即为首项相加并公差相加。
    • 显然,我们需要维护区间首项a,区间公差d和区间和s,区间和s作为区间的唯一连续性质,可视为线段树需要维护的值,那么剩下的性质(首项a和公差d)即视为懒标记。
    • 除了首项a和公差d,我们还需要维护另外一个懒标记,即区间重置标记。注意:区间重置标记的优先级高于首项a和公差d,这要求我们在下推懒标记时首先判断区间是否有重置标记,如果存在,则首项a和公差d皆需清零。
    • 下推首项a和公差d的注意事项:下推完成后依旧需要清零原标记,原因如下——因为我们将首项a和公差d当做懒标记使用,也就是将等差数列当做“数”使用(等差数列与等差数列的加和,即可类比为数与数的加和)。这一思想意味着我们唯一需要维护的只是区间和s,并且利用懒标记使时间复杂度降低(详情看代码)。
    • 类比思想:数——等差数列。

代码:

#include<cstdio>
using namespace std;
const int maxn=250001;
const long long inf=0x3f3f3f3f3f3f3f3f;
long long a[maxn<<2],s[maxn<<2],d[maxn<<2],c[maxn<<2];
bool is_reset[maxn<<2];
inline void PushUp(int rt)
{
	s[rt]=s[rt<<1]+s[rt<<1|1];
}
inline void PushDown(int rt,int ln,int rn)
{
	if(is_reset[rt])
	{
		is_reset[rt]=false;
		c[rt<<1]=c[rt<<1|1]=c[rt];
		a[rt<<1]=a[rt<<1|1]=d[rt<<1]=d[rt<<1|1]=0;
		s[rt<<1]=c[rt]*ln;
		s[rt<<1|1]=c[rt]*rn;
		is_reset[rt<<1]=is_reset[rt<<1|1]=true;
	}
	if(a[rt]||d[rt])
	{
		a[rt<<1]+=a[rt];
		a[rt<<1|1]+=a[rt]+d[rt]*ln;
		d[rt<<1]+=d[rt];
		d[rt<<1|1]+=d[rt];
		s[rt<<1]+=a[rt]*ln+ln*(long long)(ln-1)/2*d[rt];
		s[rt<<1|1]+=(a[rt]+d[rt]*ln)*rn+rn*(long long)(rn-1)/2*d[rt];
		a[rt]=d[rt]=0;
	}
}
void Update(int L,int R,long long C,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		if(C==inf)
		{
			a[rt]+=l-L+1;
			++d[rt];
			s[rt]+=(l-L+1)*(long long)(r-l+1)+(r-l+1)*(long long)(r-l)/2;
		}
		else if(C==(inf<<1))
		{
			a[rt]+=R-l+1;
			--d[rt];
			s[rt]+=(R-l+1)*(long long)(r-l+1)-(r-l+1)*(long long)(r-l)/2;
		}
		else
		{
			is_reset[rt]=true;
			a[rt]=d[rt]=0;
			c[rt]=C;
			s[rt]=(r-l+1)*C;
		}
		return;
	}
	int mid=(l+r)>>1;
	PushDown(rt,mid-l+1,r-mid);
	if(L<=mid)
	{
		Update(L,R,C,l,mid,rt<<1);
	}
	if(R>mid)
	{
		Update(L,R,C,mid+1,r,rt<<1|1);
	}
	PushUp(rt);
}
long long Query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		return s[rt];
	}
	int mid=(l+r)>>1;
	PushDown(rt,mid-l+1,r-mid);
	long long ans=0;
	if(L<=mid)
	{
		ans+=Query(L,R,l,mid,rt<<1);
	}
	if(R>mid)
	{
		ans+=Query(L,R,mid+1,r,rt<<1|1);
	}
	return ans;
}
char op[2];
const int n=250000;
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int l,r;
		scanf("%s%d%d",op,&l,&r);
		if(op[0]=='A')
		{
			Update(l,r,inf,1,n,1);
		}
		else if(op[0]=='B')
		{
			Update(l,r,inf<<1,1,n,1);
		}
		else if(op[0]=='C')
		{
			long long x;
			scanf("%lld",&x);
			Update(l,r,x,1,n,1);
		}
		else
		{
			printf("%lld\n",Query(l,r,1,n,1));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值