1. 程式人生 > 其它 >【學習筆記】P7912 [CSP-J 2021] 小熊的果籃 - 題解

【學習筆記】P7912 [CSP-J 2021] 小熊的果籃 - 題解

Acwing 245. 你能回答這些問題嗎

思路

題目問的是區間[x,y]最大連續子段和tmax

如果用線段樹求解,那麼就需要維護以下資訊

我們可以利用分治的思想,將一個子段分為左右兩個最大連續子段和,lmax,rmax;

同時需要維護左右區間的sum;那麼根節點 u.lmax = max(u.lmax,左sum+右lmax),同理維護u.rmax

那麼任意區間的最大連續和就可以分為三種情況

一種是隻在左兒子的tmax,一種是在右兒子的tmax,還有一個就是橫跨左右兒子 即 左二子的rmax+右兒子的lmax;

void pushup(Node &u,Node &l,Node &r)
{
    u.sum=l.sum+r.sum;
    u.lmax=max(l.lmax,l.sum+r.lmax);
    u.rmax=max(r.rmax,r.sum+l.rmax);
    u.tmax=max(max(l.tmax,r.tmax),r.lmax+l.rmax);
}

求得最大值即可;

那麼線段樹只需要用到單點修改和區間查詢即可

程式碼

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 5e+5;
int a[N];
struct Node
{
    int l,r;
    int lmax,rmax,tmax,sum;
    
}tre[N<<2];
void pushup(Node &u,Node &l,Node &r)
{
    u.sum=l.sum+r.sum;
    u.lmax=max(l.lmax,l.sum+r.lmax);
    u.rmax=max(r.rmax,r.sum+l.rmax);
    u.tmax=max(max(l.tmax,r.tmax),r.lmax+l.rmax);
}
void pushup(int rt)
{
    pushup(tre[rt],tre[rt*2],tre[rt*2+1]);
}
void build(int rt,int l,int r)
{
    if(l == r)
    {
        tre[rt]={l,r,a[l],a[l],a[l],a[l]};
        
    }
    else
    {   tre[rt]={l,r};
        int mid=(l+r)/2;
        build(rt*2,l,mid);
        build(rt*2+1,mid+1,r);
        pushup(rt);
    }
}
Node query(int rt,int l,int r)
{
    if(tre[rt].l>=l&&tre[rt].r<=r)
    {
        return tre[rt];
    }
    
    else
    {
        int mid = (tre[rt].l+tre[rt].r)/2;
        if(r<=mid)
        return query(rt*2,l,r);
        else if(l>mid)
        return query(rt*2+1,l,r);
        else
        {
            auto Left=query(rt*2,l,r);
            auto Right=query(rt*2+1,l,r); 
            Node res;
            pushup(res,Left,Right);
            return res;
        }
    }
}
void modify(int rt,int x,int v)
{
    if(tre[rt].l==x&&tre[rt].r==x)
    {
        tre[rt]={x,x,v,v,v,v};
        
    }
    else
    {
        int mid = (tre[rt].l+tre[rt].r)/2;
        if(x<=mid)
        modify(rt*2,x,v);
        else 
        modify(rt*2+1,x,v);
        pushup(rt);
    }
}
signed main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    {
        cin >> a[i];
    }
    build(1,1,n);
    
    while (m -- )
    {
        int k,x,y;
        cin >> k >> x >> y;
        
        
        if(k == 1)
	   { if(x>y)
	        swap(x,y);
            Node ans = query(1,x,y);
            cout<<ans.tmax<<endl;
        }
        else
        {
            modify(1,x,y);
        }
    }
    return 0;
}

246. 區間最大公約數

如果想要維護一個區間的gcd,並進行區間修改的操作很難,但如果根據gcd的這個性質

gcd(a,b)=gcd(a,b−a)

便可以用線段樹維護差分去做,區間修改就很容易了,只需要單點修改左右端點即可

重點是如何用線段樹利用差分資訊去求區間[l,r]的gcd

根據gcd(a,b,c)=gcd(a,b-a,c-b);

我們第一個需要求a是多少,那就是1-l的sum;於是線段樹需要維護一個區間sum資訊

第二個就是去維護一個區間的gcd資訊,去將gcd(b-a,c-b)求出來

void pushup(Node &u,Node &l,Node &r)
{
    u.sum=l.sum+r.sum;
    u.d=gcd(l.d,r.d);
}

程式碼

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e+5;
typedef long long ll;
ll a[N];
struct Node
{
    int l,r;
    ll sum,d;
}tre[N<<2];
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
void pushup(Node &u,Node &l,Node &r)
{
    u.sum=l.sum+r.sum;
    u.d=gcd(l.d,r.d);
}
void pushup(int rt)
{
    pushup(tre[rt],tre[rt*2],tre[rt*2+1]);
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        tre[rt]={l,r,a[l]-a[l-1],a[l]-a[l-1]};
        
    }
    else
    {
        tre[rt]={l,r};
        int mid=(l+r)/2;
        build(rt*2,l,mid);
        build(rt*2+1,mid+1,r);
        pushup(rt);
    }
}
void modify(int rt,int x,ll d)
{
    if(tre[rt].l==x&&tre[rt].r==x)
    {
        ll b=tre[rt].sum+d;
        tre[rt]={x,x,b,b};
    }
    else
    {
        int mid = (tre[rt].l+tre[rt].r)/2;
        if( x <= mid)
        modify(rt*2,x,d);
        else
        modify(rt*2+1,x,d);
        pushup(rt);
    }
}
Node query(int rt,int l,int r)
{
    if(tre[rt].l>=l&&tre[rt].r<=r)
    {
        return tre[rt];
    }
    else
    {
        int mid=(tre[rt].l+tre[rt].r)/2;
        if( r <= mid)
        {
            return  query(rt*2,l,r);
        }
        else if(l > mid)
        {
            return query(rt*2+1,l,r);
        }
        else
        {
            auto left=query(rt*2,l,r);
            auto right=query(rt*2+1,l,r);
            Node res;      
            pushup(res,left,right);
            return res;
        }
    }
}
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    build(1,1,n);
    
    while(m --)
    {
        char op[2];
        cin>>op;
        if(*op=='Q')
        {   int l,r;
            cin >> l >> r;
            Node res={0,0,0,0};
            Node ans = query(1,1,l);
            if(l+1<=r)
            res = query(1,l+1,r);
            ll se=gcd(ans.sum,res.d);
            cout<<abs(se)<<endl;
        }
        else
        {
            int l,r;
            ll d;
            cin >> l >> r >> d;
            modify(1,l,d);
            if(r+1<=n)
            modify(1,r+1,-d);
        }
    }
    
    
}

貼廣告 HDU - 2795

單點修改+區間查詢,將1-h區間以w建樹,維護一個區間能貼廣告的最大值