P4145 上帝造题的七分钟 2 / 花神游历各国

题目传送门
首先看标签,好的线段树
有区间的修改,有区间的查询,数据范围 1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1n,m105,那么就可以考虑用线段树来解。
线段树在之前的文章已经讲过了,这里就不多说,直接进入正题。
这里的区间查询就是普通的一个求和,所以就不用做什么改动。

int query(int p,int l,int r){
	if(f[p].r<l||f[p].l>r)return 0;
	if(f[p].l>=l&&f[p].r<=r){
		return f[p].ans;
	}
	return query(lc,l,r)+query(rc,l,r);
}

重点在区间修改上。
普通的区查就是加法减法乘法除法,整体的和是有规律可循的。
但是这道题是将每个元素 x x x x \sqrt{x} x ,而最后的查询是求和,所以找不到什么式子可以直接求出结果。
那不妨就暴力一点,每一遍查询就从叶子节点重新找上来。

void change(int p,int l,int r){
	if(f[p].r<l||f[p].l>r)return;
	if(f[p].l==f[p].r){
		f[p].ans=sqrt(f[p].ans);
		return;
	}
	change(lc,l,r);
	change(rc,l,r);
	up(p);
}

哦哦哦, u p up up函数在这里:

void up(int p){
	f[p].ans=f[lc].ans+f[rc].ans;
}

然就就TLE了,应为如果每一遍都从叶子结点重新找要更改的点太多了,而且许多点改了都用不上,那该怎么办?
可以从数据范围入手。

对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1n,m105 1 ≤ l , r ≤ n 1≤l,r≤n 1l,rn,数列中的数大于 0 0 0,且不超过 1 0 12 10^{12} 1012

数组中的元素最大就只能到 1 0 12 10^{12} 1012,我们对这个数不断地开更号。

1000000000000
1000000
1000
31
5
2
1

1 1 1的时候再开方就会一直是 1 1 1,所以说一个数最多开方 6 6 6次就会变成 1 1 1或者 0 0 0(当这个数是 0 0 0时),再开下去就没有任何意义了。
所以当一个数为 1 1 1 0 0 0是,我们就再也不用去管他了。同样的,当一个区间里只有 1 1 1 0 0 0时,这个区间也不用去管他了。
所以我们对刚才的暴力进行优化。

void change(int p,int l,int r){
   if(f[p].r<l||f[p].l>r)return;
   if(f[p].mx<=1)return;//当区间最大值为小于等于1时,这个区间就只有1或者0了,就不管了
   if(f[p].l==f[p].r){
   	f[p].ans=sqrt(f[p].ans);
   	f[p].mx=f[p].ans;//更新最大值
   	return;
   }
   change(lc,l,r);
   change(rc,l,r);
   up(p);
}

u p up up函数也改一下

void up(int p){
	f[p].ans=f[lc].ans+f[rc].ans;
	f[p].mx=max(f[lc].mx,f[rc].mx);
}

最后的代码:

#include<bits/stdc++.h>
#define int long long
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=1e6+5;
int n,m;
int a[N];
struct node{
   int l,r,ans,mx,add;
}f[4*N];
void up(int p){
   f[p].ans=f[lc].ans+f[rc].ans;
   f[p].mx=max(f[lc].mx,f[rc].mx);
}
void build(int p,int l,int r){
   f[p]={l,r,a[l],a[l],0};
   if(l==r)return;
   int mid=l+r>>1;
   build(lc,l,mid);
   build(rc,mid+1,r);
   up(p);
}
void change(int p,int l,int r){
   if(f[p].r<l||f[p].l>r)return;
   if(f[p].mx<=1)return;
   if(f[p].l==f[p].r){
   	f[p].ans=sqrt(f[p].ans);
   	f[p].mx=f[p].ans;
   	return;
   }
   change(lc,l,r);
   change(rc,l,r);
   up(p);
}
int query(int p,int l,int r){
   if(f[p].r<l||f[p].l>r)return 0;
   if(f[p].l>=l&&f[p].r<=r){
   	return f[p].ans;
   }
   return query(lc,l,r)+query(rc,l,r);
}
signed main(){
   ios::sync_with_stdio(0);
   cin>>n;
   for(int i=1;i<=n;i++)cin>>a[i];
   build(1,1,n);
   int x,y;
   cin>>m;
   while(m--){
   	int op;
   	cin>>op;
   	cin>>x>>y;
   	if(x>y)swap(x,y);//x可能大于y
   	if(op==0){
   		change(1,x,y);
   	}
   	else{
   		cout<<query(1,x,y)<<'\n';
   	}
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值