2020 CCPC-Wannafly-Winter-Camp Day5 C Self-Adjusting Segment Tree

好久都懒得写题解,直到今天跟着题解写完这道题,发现这道题也太太太太太妙了,被美妙的做法震惊到了,来记录一下。

题目大意:
对于一颗区间范围为n的线段树,你可以对每个区间的断点进行重新分配,如区间 l , r l, r l,r 可以在 [ l , r ) [l, r) [l,r) 中任选一个k作为断点使得两个儿子分别为 [ l , k ] [l, k] [l,k] [ k + 1 , r ] [k+1, r] [k+1,r]
给m次询问,每次询问为一个区间,求在线段树查询时总共需要经过的最少节点个数。
数据范围n 500,m 2e5

这题精妙的地方便在于对线段树节点的理解上,首先我们知道在线段树上进行 [ l , r ] [l, r] [l,r] 区间查询时,对于每个区间 [ L , R ] [L, R] [L,R] ,如果 [ L , R ] [L, R] [L,R] [ l , r ] [l, r] [l,r] 有交错区间或且 [ L , R ] [L, R] [L,R] 不被 [ l , r ] [l, r] [l,r] 包含,那么这个节点一定被经过一次,剩下的便是被 [ l , r ] [l, r] [l,r] 包含的区间,这样的区间一定是一次访问的最后一层。
首先,线段树的一个节点 [ L , R ] [L, R] [L,R] 的子树大小为 ( R − L + 1 ) ∗ 2 − 1 (R-L+1)*2-1 (RL+1)21,其中叶节点个数为 ( R − L + 1 ) (R-L+1) (RL+1),剩下的非叶节点个数为 ( R − L ) (R-L) (RL)。剩下的被 [ l , r ] [l, r] [l,r] 包含的区间无非是叶节点以及非叶节点两种,他们的数量差值恰好为1。
如此一来,我们就可以将所有被 [ l , r ] [l, r] [l,r] 包含的非叶节点贡献记为 − 1 -1 1,所有被 [ l , r ] [l, r] [l,r] 包含的叶节点贡献记为 + 1 +1 +1, 与 [ l , r ] [l, r] [l,r] 有交错区间且不被 [ l , r ] [l, r] [l,r] 包含的节点贡献记为 + 1 +1 +1,这样所有被包含的节点的子树贡献和总为1,而经过的有交错的节点贡献也为1

这样,我们就可以将所有种类的区间贡献处理出来,剩下的就是一个区间合并问题了,可以用一个显然的dp解决。

#include <bits/stdc++.h>
using namespace std;

const int maxn=505;
long long dp[maxn][maxn],a[maxn][maxn];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n,m;
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int l,r;
        cin>>l>>r;
        for(int j=1;j<=r;j++){
            if(j<l) a[j][l]++;
            else{
                a[j][r+1]+=2;
                a[j][j]++;
                a[j][j+1]-=2;
            }
        }
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            a[i][j]+=a[i][j-1],dp[i][i]=a[i][i];
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            for(int k=i;k<j;k++){
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+a[i][j]);
            }
        }
    }
    cout<<dp[1][n]<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值