数据结构(势能线段树)
https://www.luogu.com.cn/problem/P10516
第3题 数据结构 查看测评数据信息
给定两个长度为 n 的序列 a[i] 和 b[i]。有以下三种操作:
1. 给定区间 [l,r] 以及参数 k,t,把区间内满足 a[i]* b[i]<= k 的位置的 a[i] 和 b[i] 分别加上 t。
2. 给定 i 和 x,y,将 a[i] 改为 x,b[i] 改为 y。
3. 查询区间内每个位置 a[i]+b[i] 的和。
输入格式
第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示 a[i]。
第三行包含 n 个用空格分隔的整数,其中第 i 个数字表示 b[i]。
接下来 m 行每行包含 3 到 5 个整数,表示一个操作,具体如下:
1. 1 l r k t:将区间 [l,r] 进行一操作。
2. 2 i x y:将 a[i] 改为 x,b[i] 改为 y。
3. 3 l r:输出区间 [l,r] 内每个数的和。
1<= n,m<= 1e5,0<= a[i],b[i],k,t,x,y<=1e5
输出格式
若干行,每行表示操作 3 的答案。
输入/输出例子1
输入:
5 5
23 4 3 3 7
54 29 7 1 2
1 1 5 114 1
2 2 7 9
3 1 5
3 1 2
3 3 4
输出:
122
93
18
样例解释
第一次修改后,序列 a[i] 为:{23,4,4,4,8};序列 b[i] 为 {54,29,8,2,3}。
第二次修改后,序列 a[i] 为:{23,7,4,4,8};序列 b[i] 为 {54,9,8,2,3}。
势能线段树概念(重要)
本质上是一种区间暴力修改技术。
考虑普通的区修线段树,我们使用 lazytag 来记录区间修改信息,在访问到当前节点之后再将当前节点的 tag 下传。
然而,有很多信息是不好下传的(不满足交换或结合律),于是我们就无法使用 tag 来记录区修信息。但有的题目每一个操作都有潜在的操作次数上限,我们可以先判断该区间有没有需要操作的节点,有的话遍历到叶子节点进行修改,没有的话返回。
单调操作,区间查询,很容易想到线段树
操作二,三都是板子,我们关注一下操作一
分析一下操作一
看上去是区间修改,但是下放lazy_tag发现很难,对于修改也很难(不知具体改哪一个点),原因就是此操作不满足区间性(区间的每个值同时加减乘除等等吧)。所以考虑单点修改
由于每次操作完
a[i]*b[i] 都变成 (a[i]+t)*(b[i]+t)
我们化简。a[i]*b[i] + a[i]*t + b[i]*t + t*t
注意到最后一项,这样就说明这个值的增长是非常快的,操作次数不超过 sqrt(k) 就可以到上限
但是注意,这里 t不为0,才能增长,否则就是无意义的。
还有一点,要维护一个区间最小值,因为只有<=k的值才能进行修改。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
struct node
{
int Min, sum;
}tr[N];
int n, m, x, y, op, a[N], b[N], L, R, k, t;
void push_up(int id)
{
tr[id].sum=tr[id*2].sum+tr[id*2+1].sum;
tr[id].Min=min(tr[id*2].Min, tr[id*2+1].Min);
}
void build(int id, int L, int R)
{
if (L==R)
{
tr[id].sum=a[L]+b[L];
tr[id].Min=a[L]*b[L];
return ;
}
int mid=(L+R)>>1;
build(id*2, L, mid);
build(id*2+1, mid+1, R);
push_up(id);
}
void change(int id, int L, int R, int x, int y, int k, int t)
{
if (tr[id].Min>k) return ;
if (L==R)
{
a[L]+=t;
b[L]+=t;
tr[id].sum=a[L]+b[L];
tr[id].Min=a[L]*b[L];
return ;
}
int mid=(L+R)>>1, flag=1;
if (x<=mid) change(id*2, L, mid, x, y, k, t);
if (y>=mid+1) change(id*2+1, mid+1, R, x, y, k, t);
push_up(id);
}
void change2(int id, int L, int R, int x, int v, int v2)
{
if (L==R)
{
a[x]=v;
b[x]=v2;
tr[id].sum=a[L]+b[L];
tr[id].Min=a[L]*b[L];
return ;
}
int mid=(L+R)>>1;
if (x<=mid) change2(id*2, L, mid, x, v, v2);
else change2(id*2+1, mid+1, R, x, v, v2);
push_up(id);
}
int ask(int id, int L, int R, int x, int y)
{
if (x<=L && R<=y) return tr[id].sum;
int mid=(L+R)>>1, res=0;
if (x<=mid) res+=ask(id*2, L, mid, x, y);
if (y>=mid+1) res+=ask(id*2+1, mid+1, R, x, y);
return res;
}
signed main()
{
scanf("%lld%lld", &n, &m);
for (int i=1; i<=n; i++) scanf("%lld", &a[i]);
for (int i=1; i<=n; i++) scanf("%lld", &b[i]);
build(1, 1, n);
while (m--)
{
scanf("%lld", &op);
if (op==1)
{
scanf("%lld%lld%lld%lld", &L, &R, &k, &t);
if (t==0) continue;
change(1, 1, n, L, R, k, t);
}
else if (op==2)
{
scanf("%lld%lld%lld", &k, &L, &R);
change2(1, 1, n, k, L, R);
}
else
{
scanf("%lld%lld", &L, &R);
printf("%lld\n", ask(1, 1, n, L, R));
}
}
return 0;
}

浙公网安备 33010602011771号