資料結構(勢能線段樹)

cn是大帅哥886發表於2024-08-20

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;
}

  

相關文章