今天和大家分享几道关于简单的线段树的暴力修改的题目~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);
}
}