CodeForces - 121E Lucky Array(线段树)

题干:

给你n个数a[i],和m组询问。询问包括两种格式add l,r,val和count l,r:
add表示将区间[l,r]的数都加上val;
count表示查询区间[l,r]的数中有多少幸运数字(幸运数字仅由4 米斯达点了个? 和7组成
(如:4,7,47,4477;147则不是))

思路:

区间修改,区间查询,一般就是线段树或者树状数组了。
然后就是要明确维护的值是什么值,首先要维护区间内幸运数字的个数,然后因为要高效的求出修改后的值,所以需要维护区间内的数到幸运数字的距离的最小值及个数,然后每次更新完数据后要根据标记稍微重建一下树。
详细操作见代码…

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <math.h>
#include <queue>
#include <stack>  
using namespace std;
typedef long long ll;
const int mx =110000;
int luck[32]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477,
4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777,44444,44447}; //幸运数字数组
ll dis[11000],a[mx];
struct stu
{
	int l,r;
	int x,y,lazy;
}tree[4*mx];
void pushup(int x)  //更新数组的区间值
{
	int t1=x<<1,t2=(x<<1)+1;
	if(tree[t1].x==tree[t2].x){
		tree[x].x=tree[t1].x;
		tree[x].y=tree[t1].y+tree[t2].y;
	}
	else if(tree[t1].x>tree[t2].x){
		tree[x].x=tree[t2].x;
		tree[x].y=tree[t2].y;
	}
	else{
		tree[x].x=tree[t1].x;
		tree[x].y=tree[t1].y;
	}
}
void build(int x,int l,int r)
{
	tree[x].l=l;
	tree[x].r=r;
	tree[x].lazy=0;
	if(l==r){
		tree[x].x=dis[a[l]];
		tree[x].y=1;
		return;
	}
	int m=(l+r)>>1;
	build(x<<1,l,m);
	build((x<<1)+1,m+1,r);
	pushup(x);
}
void pushdown(int x)
{
	if(tree[x].lazy)  //懒标记下放
	{
		int t1=x<<1,t2=(x<<1)+1;
		tree[t1].lazy+=tree[x].lazy;
		tree[t2].lazy+=tree[x].lazy;
		tree[t1].x+=tree[x].lazy;
		tree[t2].x+=tree[x].lazy;
		tree[x].lazy=0;
	}
}
void updata(int x,int l,int r,int val)
{
	if(l<=tree[x].l&&tree[x].r<=r)
	{
		tree[x].x+=val;
		tree[x].lazy+=val;
		return;
	}
	pushdown(x);
	int m=(tree[x].l+tree[x].r)>>1;
	if(m>=r)	updata(x<<1,l,r,val); 
	else if(l>m)	updata(x<<1|1,l,r,val); 
	else
	{
		updata(x<<1,l,m,val);
		updata(x<<1|1,m+1,r,val);	
	}
	pushup(x);
}
int que(int x,int l,int r)
{
	if(tree[x].x)	return 0;
	if(l<=tree[x].l&&tree[x].r<=r)
		return tree[x].y;
	pushdown(x);
	int m=(tree[x].l+tree[x].r)>>1;
	if(m>=r)	return	que(x<<1,l,r); 
	if(l>m)	return que(x<<1|1,l,r); 
	return	que(x<<1,l,m)+que(x<<1|1,m+1,r);
}
void rebuild(int x)
{
	if(tree[x].x>=0)  return;   //如果距离为正则不用更新
	if(tree[x].l==tree[x].r){
		a[tree[x].l]-=tree[x].lazy;  //更新根的值
		tree[x].x=dis[a[tree[x].l]];  //得到新的距离
		tree[x].lazy=0;
		return;
	}
	pushdown(x);
	rebuild(x<<1);
	rebuild((x<<1)+1);
	pushup(x);
}
int main()
{
	int n,m,now=0,x,y,z;
	char ch[10];
	scanf("%d%d",&n,&m);
	for(int i=1;i<=10000;i++){
		if(i>luck[now])	now++;    
		dis[i]=luck[now]-i;
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	build(1,1,n);
	for(int i=0;i<m;i++){
		scanf("%s",ch);
		if(ch[0]=='a'){
			scanf("%d%d%d",&x,&y,&z);
			updata(1,x,y,-z);   //因为是加法所以距离变小
			rebuild(1);  //从头开始遍历是否要重建树
		}
		else if(ch[0]=='c'){
			scanf("%d%d",&x,&y);
			printf("%d\n",que(1,x,y));
		}
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值