Codeforces Round 881 (Div. 3)F1题解

Omsk Metro (simple version)

在这里插入图片描述在这里插入图片描述

问题分析

给定一个树形结构的起始点,然后有q次操作,每次操作有两种,一种为向该树形结构已存在的点中添加结点,另一种为查询根节点到某个已存在结点中是否存在一个连续子段其和为k。每个结点的都有一个权值,权值为-1或者1。

1.分析如何知道根节点到某个结点的区间内是否存在一个子段和为k

假设该区间内存在一个子段其和为k,考虑该子段和相邻子段的关系,若将该子段进行扩展,由于每个结点权值为-1或者1,则每次扩展一个结点后比扩展前的和要多1或者少1,即值的增减是连续的。则对于整个区间而言存在一个子段和为k等价于,k属于该区间内最大子段和到最小子段和的范围内。

2.方法1使用树形DP来动态维护每个节点到根节点的最大子段和和最小子段和

由于新增节点后其子段和的最值只涉及其父节点,则可以采用树形DP来动态维护所需的值。对于一个节点,其状态定义有两个一个为 f 1 ( v ) f1(v) f1(v)表示1到v区间中的最大子段和,另一个位 f 2 ( v ) f2(v) f2(v)为表示1到v区间中的最小子段和。状态转移的方式有两类,一类为包括v点,一类为不包括v点, f ( v , 0 ) f(v,0) f(v,0)为不包括v点, f ( v , 1 ) f(v,1) f(v,1)为包括v点。

不包括v点的,可以由包含父节点的子段,不包含父节点的子段,以及都不包含3种状态转移而来。 f ( v , 0 ) = f(v,0)= f(v,0)={ f ( u , 0 ) , f ( u , 1 ) f(u,0),f(u,1) f(u,0),f(u,1),0}

包括v点的,可以由包含父节点的子段加上当前点的值,仅有当前点的值,2种状态转移而来。 f ( v , 1 ) = f(v,1)= f(v,1)={ f ( u , 1 ) + v a l , v a l f(u,1)+val,val f(u,1)+val,val}

代码
#include<bits/stdc++.h>

#define x first
#define y second
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N = 2e5 + 10, M = N << 6, INF=0x3f3f3f3f;
int f1[N][2],f2[N][2];

void solve() {
    int n;
    cin >>n;
    ///由于每个结点只会在添加时通过其父节点更新,故只需初始化根节点即可
    f1[1][1]=1,f1[1][0]=0;
    f2[1][1]=1,f2[1][0]=0;

    char op[2];
    int idx=1;///节点编号
    for(int i=0;i<n;i++){
        scanf("%s",op);
        if(op[0]=='+'){
            int u,w;
            scanf("%d %d",&u,&w);
            ++idx;
            f1[idx][0]=max({f1[u][0],f1[u][1],0});
            f1[idx][1]=max({f1[u][1]+w,w});

            f2[idx][0]=min({f2[u][0],f2[u][1],0});
            f2[idx][1]=min({f2[u][1]+w,w});
        }else {
            int u,v,k;
            scanf("%d %d %d",&u,&v,&k);
            int minval=min(f2[v][0],f2[v][1]);
            int maxval=max(f1[v][0],f1[v][1]);
            if(k>=minval&&k<=maxval)  puts("YES");
            else puts("NO");
        }
    }
}

int main() {
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值