线段树--暴力修改专题浅谈

今天和大家分享几道关于简单的线段树的暴力修改的题目~hh

1.区间开方
洛谷p4145  花神游历各国

输入格式

第一行一个整数 n,代表数列中数的个数。

第二行 n 个正整数,表示初始状态下数列中的数。

第三行一个整数 m,表示有 m 次操作。

接下来 m 行每行三个整数 k l r

  • k=0 表示给 [l,r]中的每个数开平方(下取整)。

  • k=1 表示询问 [l,r] 中各个数的和。

数据中有可能 l>r,所以遇到这种情况请交换 l和 r。

输出格式

对于询问操作,每行输出一个回答。

输入输出样例

输入 #1复制

10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8

输出 #1复制

19
7
6

思路:首先因为刚学线段树的缘故,看到区间修改我就想肯定是写一个lazy,但是一想开平方再相加和相加再开平方是不恒等的,那怎么办呢,想起来学长提过一句势能线段树,首先lazy的设置的确是为了让我们的修改操作优化成logn,但是一看这个开平方的操作一个数(1e12)很快就能开到1从一再往后开的话就没有意义的,因此这启发我们只需要维护一下区间的最大值,只要区间的最大值已经是1了就可以直接跳出去不必再继续开平方了,这样我们的复杂度也就来到了n  logn*一个很小的常数。唯一需要注意的就是开始的数组注意开longlong。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long LL;
struct Node{
	int l,r;
	LL sum;
	LL tmax;
}tr[N<<2];
int n,m;
LL w[N];
void pushup(int u)
{
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
	tr[u].tmax=max(tr[u<<1].tmax,tr[u<<1|1].tmax);
}
void build(int u,int l,int r)
{
	if(l==r)tr[u]={l,r,w[l],w[l]};
	else
	{
		tr[u]={l,r};
		int mid=l+r>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	} 
}
void modify(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r)
	  if(tr[u].tmax<=1)return;
	  
	if(tr[u].l==tr[u].r)tr[u].sum=tr[u].tmax=sqrt(tr[u].sum);
	else
	{
		int mid=tr[u].l+tr[u].r>>1;
		if(l<=mid)modify(u<<1,l,r);
		if(r>mid)modify(u<<1|1,l,r);
		pushup(u);
	}
}
LL query(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
	int mid=tr[u].l+tr[u].r>>1;
	if(r<=mid)return query(u<<1,l,r);
	else if(l>mid)return query(u<<1|1,l,r);
	else return query(u<<1,l,r)+query(u<<1|1,l,r);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
	cin>>m;
	build(1,1,n);
	while(m--)
	{
		LL k,l,r,d;
		scanf("%lld%lld%lld",&k,&l,&r);
		if(l>r)swap(l,r);
		if(!k)
          modify(1,l,r);
        else
         printf("%lld\n",query(1,l,r));
	}
}




2.区间取模
CF438D
 D. The Child and Sequence

Input

The first line of input contains two integer: n, m (1 ≤ n, m ≤ 105). The second line contains n integers, separated by space: a[1], a[2], ..., a[n] (1 ≤ a[i] ≤ 109) — initial value of array elements.

Each of the next m lines begins with a number type 

.

  • If type = 1, there will be two integers more in the line: l, r (1 ≤ l ≤ r ≤ n), which correspond the operation 1.
  • If type = 2, there will be three integers more in the line: l, r, x (1 ≤ l ≤ r ≤ n; 1 ≤ x ≤ 109), which correspond the operation 2.
  • If type = 3, there will be two integers more in the line: k, x (1 ≤ k ≤ n; 1 ≤ x ≤ 109), which correspond the operation 3.

Output

For each operation 1, please print a line containing the answer. Notice that the answer may exceed the 32-bit integer.

Examples

input

Copy

5 5
1 2 3 4 5
2 3 5 4
3 3 5
1 2 5
2 1 3 3
1 1 3

output
8
5




思路:有了上一题的铺垫,这个题就变得很裸了,主要是这个暴力修改的思想和适用场景比较重要
下面是代码:


 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,m;
int w[N];
struct Node
{
	int l,r;
	LL tmax;
	LL sum;
}tr[4*N];
void pushup(int u)
{
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
	tr[u].tmax=max(tr[u<<1].tmax,tr[u<<1|1].tmax);
}
void build(int u,int l,int r)
{
	if(l==r)tr[u]={l,l,w[l],w[l]};
	else 
	{
		tr[u]={l,r};
		int mid=l+r>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	}
	
}
void modify1(int u,int l,int r,LL p)
{
	if(tr[u].l>=l&&tr[u].r<=r)
	 if(tr[u].tmax<p)return;
	 //暴力到叶子节点 
	if(tr[u].l==tr[u].r)
	{
		tr[u].sum%=p;
		tr[u].tmax%=p;
	}
	else 
	{
		int mid=tr[u].l+tr[u].r>>1;
		if(l<=mid)modify1(u<<1,l,r,p);
		if(r>mid)modify1(u<<1|1,l,r,p);
		pushup(u);
	}
}
void modify2(int u,int x,LL v)
{
	
	if(tr[u].l==x&&tr[u].r==x)tr[u].sum=tr[u].tmax=v;
	else
	{
		int mid=tr[u].l+tr[u].r>>1;
		if(x<=mid)modify2(u<<1,x,v);
		else modify2(u<<1|1,x,v);
		pushup(u);
	}
}
LL query(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
	int mid=tr[u].l+tr[u].r>>1;
	if(r<=mid)return query(u<<1,l,r);
	else if(l>mid)return query(u<<1|1,l,r);
	else  return query(u<<1,l,r)+query(u<<1|1,l,r);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&w[i]);
	build(1,1,n);
	while(m--)
	{
		LL k,l,r,c;
		scanf("%lld%lld%lld",&k,&l,&r);
		if(k==1)printf("%lld\n",query(1,l,r));
		else if(k==2)
		{
			scanf("%lld",&c);
			modify1(1,l,r,c);
		}
		else modify2(1,l,r);
	}
	
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
廊桥分配问题是指给定一座长度为n的廊桥以及m个人,每个人需要跨过廊桥到对面。廊桥每次只能让两个人同时通过,且只有两个人的速度加和不超过廊桥长度时才能通过。每个人过桥所需的时间不同,要求找到一种过桥方案使得所有人的总过桥时间最短。 该问题可以通过使用线段的解法。首先,将n个位置看作是一棵,每个节点对应一个位置。然后,我们将所有人按照过桥时间从小到大排序,并按照排序结果为每个节点分配一个排序编号。接下来,从左到右遍历排序后的人员列表,对于每个人,我们找到其对应的节点,并为该节点分配一个值,表示该位置可以被占用。 这样,在分配完所有人的节点后,我们得到了一个线段,每个非叶子节点表示一个廊桥位置,叶子节点表示一个人,其父节点的值表示桥上人员的速度加和。通过遍历这颗,可以计算出所有人过桥的最短总时间。 具体操作如下: 1. 根据所有人的过桥时间从小到大排序。 2. 为每个节点分配排序编号。 3. 初始化线段的所有节点为空(未占用)。 4. 从左到右遍历排序后的人员列表,对于每个人: a. 找到对应的节点。 b. 判断该节点是否为空,如果为空,表示该位置可以被占用,否则找到该节点的兄弟节点(该节点的父节点的其他子节点)。 c. 将该节点或其兄弟节点标记为占用,并更新父节点的值。 5. 遍历线段,计算所有人过桥的总时间。 使用线段解决廊桥分配问题的时间复杂度为O(nlogn),因为排序的时间复杂度为O(nlogn),遍历人员列表的时间复杂度为O(n),遍历线段的时间复杂度为O(nlogn)。总的空间复杂度为O(n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值