LOJ #3277. 「JOISC 2020 Day3」星座 3 (笛卡尔树+线段树合并)

笛卡尔树好神奇啊!  

我们考虑对白色区域维护以最大值为中点笛卡尔树,然后考虑怎么合并左右区间:  

令 $f[h]$ 表示最高高度为 $h$ 的最大保留权和,v 表示当前笛卡尔树节点的权值.  

考虑如何合并左右区间:

显然,如果要将 $f[y]$ 贡献给 $f[x]$,那么 $x$ 要大于 $y.$ 

我们将左右区间的所有状态分成大于等于 $v$ 和小于等于 $v$.   

因为要求任意不包含白点的矩阵只能有一个星星,所以左面小于等于 $v$ 的可以完全贡献给右面大于等于 $v$ 的(左面大于 $v$ 的贡献不了) 

这个在线段树上就是一个区间标记下传.    

然后对于小于 $v$ 的情况比较难处理,但是我们发现笛卡尔树中父亲的权值大于等于当前权值,所以小于 $v$ 的所有情况只保留一个就行(既左max+右max 赋给 $f[v-1]$)    

然后来一个线段树合并合并一下状态就行. 

code: 

#include <bits/stdc++.h>   
#define N 200007   
#define ll long long 
#define lson s[x].ls 
#define rson s[x].rs  
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;      
int tot,n,top;     
int ch[N][2],a[N],val[N],sta[N],size[N],RT[N];       
int newnode() { return ++tot; }    
struct data { int h,c; data(int h=0,int c=0):h(h),c(c){}  };    
struct node { int ls,rs;ll ma,ad; }s[N*80];                       
vector<data>ge[N];   
void mark(int x,ll v) { s[x].ma+=v,s[x].ad+=v;  }      
void pushdown(int x) 
{
    if(s[x].ad) 
    {
        if(lson) mark(lson,s[x].ad);  
        if(rson) mark(rson,s[x].ad); 
        s[x].ad=0; 
    }
}
void update(int &x,int l,int r,int p,ll v) 
{
    if(!x) x=newnode();   
    s[x].ma=max(s[x].ma,v);             
    if(l==r) return;   
    int mid=(l+r)>>1;  
    pushdown(x); 
    if(p<=mid)  update(lson,l,mid,p,v);  
    else update(rson,mid+1,r,p,v); 
}
ll query(int x,int l,int r,int L,int R) 
{
    if(!x) return 0; 
    if(l>=L&&r<=R) return s[x].ma; 
    pushdown(x); 
    int mid=(l+r)>>1; ll re=0;  
    if(L<=mid) re=max(re,query(lson,l,mid,L,R));  
    if(R>mid)  re=max(re,query(rson,mid+1,r,L,R));  
    return re;   
}
void addv(int x,int l,int r,int L,int R,ll v) 
{
    if(!x) return;  
    if(l>=L&&r<=R) { mark(x,v); return; }    
    pushdown(x);  
    int mid=(l+r)>>1;   
    if(L<=mid)  addv(lson,l,mid,L,R,v);         
    if(R>mid)   addv(rson,mid+1,r,L,R,v);             
    s[x].ma=max(s[lson].ma,s[rson].ma);     
}
int merge(int x,int y) 
{
    if(!x||!y) return x+y;   
    pushdown(x),pushdown(y);     
    int now=newnode();  
    s[now].ma=max(s[x].ma,s[y].ma);  
    s[now].ls=merge(s[x].ls,s[y].ls);     
    s[now].rs=merge(s[x].rs,s[y].rs); 
    return now; 
}   
void dfs(int x) 
{
    size[x]=1;  
    if(ch[x][0]) dfs(ch[x][0]),size[x]+=size[ch[x][0]];            
    if(ch[x][1]) dfs(ch[x][1]),size[x]+=size[ch[x][1]];     
}                 
void mer(int x,int y,int tmp) 
{
    ll a=query(RT[x],0,n,0,tmp);    
    ll b=query(RT[y],0,n,0,tmp);       
    addv(RT[x],0,n,tmp,n,b);   
    addv(RT[y],0,n,tmp,n,a);  
    ll c=query(RT[x],0,n,0,tmp-1);  
    ll d=query(RT[y],0,n,0,tmp-1);     
    update(RT[x],0,n,tmp-1,c+d);        
    RT[y]=merge(RT[x],RT[y]);   
}
void solve(int x) 
{
    for(int i=0;i<ge[x].size();++i) 
        update(RT[x],0,n,ge[x][i].h,ge[x][i].c);        
    if(ch[x][0])  
        solve(ch[x][0]),mer(ch[x][0],x,a[x]);  
    if(ch[x][1])  
        solve(ch[x][1]),mer(ch[x][1],x,a[x]);          
}
int main() 
{ 
    // setIO("input");  
    scanf("%d",&n);    
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);    
    for(int i=1;i<=n;++i) 
    {
        while(top&&a[i]>=a[sta[top]])         
            ch[i][0]=sta[top],--top;      
        if(top) ch[sta[top]][1]=i;      
        sta[++top]=i;  
    }     
    int rt=sta[1],m;   
    ll t=0;   
    scanf("%d",&m);  
    for(int i=1;i<=m;++i) 
    {
        int x,y,c;  
        scanf("%d%d%d",&x,&y,&c);     
        ge[x].push_back(data(y,c));  
        t+=(ll)c;  
    }    
    dfs(rt);      
    solve(rt);        
    printf("%lld
",t-query(RT[rt],0,n,0,n));       
    return 0; 
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12858472.html