【学习笔记】[JOISC2020] 星座 3

一眼笛卡尔树,这样我们得到了 O ( n m ) O(nm) O(nm) D P DP DP做法。

有一步 非常关键 的转化:考虑求能保留下来的星座的最大价值。

为什么要这么转化?因为这样好算贡献!

考虑优化,设 d p i , j dp_{i,j} dpi,j表示 i i i为根的子树,只考虑高度 ≤ j \le j j的星星,能保留下来的星座的最大价值。

写出 D P DP DP式子:

1.1 1.1 1.1 d p i , j = d p l , j + d p r , h i dp_{i,j}=dp_{l,j}+dp_{r,h_i} dpi,j=dpl,j+dpr,hi
1.2 1.2 1.2 d p i , j = d p r , j + d p l , h i dp_{i,j}=dp_{r,j}+dp_{l,h_i} dpi,j=dpr,j+dpl,hi
1.3 1.3 1.3 d p i , j = d p l , h i + d p r , h i + val ( i , j ) dp_{i,j}=dp_{l,h_i}+dp_{r,h_i}+\text{val}(i,j) dpi,j=dpl,hi+dpr,hi+val(i,j)

发现合法状态数目其实是 区间内星星的数量,并且 只需要保留 j ≥ h i j\ge h_i jhi部分的 D P DP DP 即可。

这个形式非常优美,直接写一发线段树合并即可。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

remark \text{remark} remark 第一步转化真的非常重要!!否则后面的 D P DP DP式子有很大一堆!就因为这个我自闭了一天。

#include
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=2e5+5;
const int M=N*40;
int n,m,tot,rt[N],h[N];
int nxt[N],sum[N];
//建立笛卡尔树即可
struct node{
    int l,r;
    ll max,add;
}t[M];
vector<pair<int,int>>points[N];
int s[N],cnt,ls[N],rs[N];
void build(){
    for(int i=1;i<=n;i++){
        int tmp=0;
        while(cnt&&h[s[cnt]]<h[i])tmp=s[cnt--];
        ls[i]=tmp;if(cnt)rs[s[cnt]]=i;
        s[++cnt]=i;
    }
}
void add(int p,ll x){
    if(!p)return;t[p].max+=x,t[p].add+=x;
}
void pushup(int p){
    t[p].max=max(t[t[p].l].max,t[t[p].r].max);
}
void pushdown(int p){
    if(t[p].add)add(t[p].l,t[p].add),add(t[p].r,t[p].add),t[p].add=0;
}
ll query(int p,int l,int r,int ql,int qr){
    if(!p||ql>qr)return 0;
    if(ql<=l&&r<=qr)return t[p].max;
    int mid=l+r>>1;pushdown(p);
    if(qr<=mid)return query(t[p].l,l,mid,ql,qr);
    if(mid<ql)return query(t[p].r,mid+1,r,ql,qr);
    return max(query(t[p].l,l,mid,ql,qr),query(t[p].r,mid+1,r,ql,qr));
}
void modify(int &p,int l,int r,int x,ll y){
    if(!p)p=++tot;
    if(l==r){
        t[p].max=max(t[p].max,y);
        return;
    }
    int mid=l+r>>1;pushdown(p);
    x<=mid?modify(t[p].l,l,mid,x,y):modify(t[p].r,mid+1,r,x,y);
    pushup(p);
}
int merge(int p,int q,int l,int r){
    if(!p||!q)return p+q;
    if(l==r){
        t[p].max=max(t[p].max,t[q].max);
        return p;
    }
    int mid=l+r>>1;pushdown(p),pushdown(q);
    t[p].l=merge(t[p].l,t[q].l,l,mid),t[p].r=merge(t[p].r,t[q].r,mid+1,r);
    pushup(p);return p;
}
void solve(int u){
    if(!u)return;
    int l=ls[u],r=rs[u];
    solve(l),solve(r);
    ll c1=query(rt[l],1,n,1,h[u]),c2=query(rt[r],1,n,1,h[u]);
    add(rt[l],c2),add(rt[r],c1);
    for(auto x:points[u])modify(rt[u],1,n,x.fi,x.se+c1+c2);
    modify(rt[u],1,n,h[u],c1+c2);
    rt[u]=merge(merge(rt[l],rt[r],1,n),rt[u],1,n);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;for(int i=1;i<=n;i++)cin>>h[i];
    ll sum=0;
    cin>>m;for(int i=1;i<=m;i++){
        int x,y,c;cin>>x>>y>>c;sum+=c;
        points[x].pb({y,c});
    }
    build();solve(s[1]);cout<<sum-t[rt[s[1]]].max;
}

你可能感兴趣的:(学习,笔记)