HDU6161

7 篇文章 0 订阅

hdu6161 Big binary tree

多校1001
题目链接
题目是给一棵完全二叉树,从上到下从左到右给每个节点标号,每个点有权值,初始权值为其标号,然后有两种操作:
1、把u点权值改为x
2、查询所有经过u点的路径中,路径上的点权和最大。
节点有n个,修改有m个,n<=1e8 ,m<= 1e5
我们容易想到维护一个dp[u]代表从u点往下走能获得最大的权值

dp[u] = max(dp[u<<<1] , dp[u<<1|1]) + a[u] ;(a[u]为其本身的权值)

当我们修改某个点的权值时,只要更新其父节点到根节点的权值即可,
然后查询某个点时,由于所有节点都是正数,那么路径选的越长越好,所以在纸上画一下可以知道,这条路径可能是以该点为根的子树,从左子树的叶子走到右子树的叶子,也就是dp[u] = dp[u<<1|1] + dp[u<<1] + a[u] ;也有可能从这个点再往上走,然后走到某个点时往当前同父的另一个子节点走(用纸上画一下可以知道)
但是!!!最坑的是这个节点数非常大,会爆空间,但是我们发现如果一个点,其子树上的点都没被修改过,那么它的a[u],dp[u]都是可以通过初始值算出来的,也就是不需要开这部分空间,我们只关心询问中涉及到的点 。询问中会涉及到的点数最多<=1e5*(log n)*2,那么我们可以把这部分点放到map里,然后其他不涉及到的点都是可以直接算出来的 。

#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const LONG  INF=0x3f3f3f3f;
const LONG  MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)|1
#define root 1, n , 1
LONG n , m ;
map<LONG ,LONG > dp ;
map<LONG ,LONG > a;
LONG Get_dp(LONG u)
{
    LONG tmp = u ;
    LONG res = 0 ;
    while(tmp <= n)
    {
        res += tmp ;
        tmp = tmp*2 + 1;
    }
    LONG ret = 0 ;
    tmp = n ;
    int judge = 0 ;
    while(tmp)
    {
        ret += tmp ;
        if(tmp == u)
        {
            judge = 1;
            break ;
        }
        tmp /= 2;
    }
    if(judge )
        return max (ret ,res) ;
    else return res ;
}
int main()
{
    while(cin >> n >>m )
    {
        dp.clear () ;
        a.clear() ;
        char op[10] ;
        while(m -- )
        {
            scanf("%s",op) ;
            LONG  u ;
            if(op[0] =='q')
            {
                scanf("%lld",&u) ;
                LONG f1 ,f2 ,f3 ;
                if(dp[u<<1] == 0)
                    f1 = Get_dp(u<<1 ) ;
                else
                    f1 = dp[u<<1] ;
                if(dp[u<<1|1] == 0)f2 = Get_dp(u<<1|1) ;
                else f2 = dp[u<<1|1] ;
                if(a[u] == 0)f3 = u ;
                else f3 = a[u] ;
                LONG ans = f1 + f2 + f3 ;
                LONG tmp = u ;
                LONG res ;
                if(dp[u] == 0) res = Get_dp(u) ;
                else res = dp[u] ;
                 while(tmp>1ll)
                {
                    if(dp[tmp-1] == 0) f1 = Get_dp(tmp-1) ;
                    else f1 = dp[tmp-1] ;
                    if(dp[tmp+1] == 0) f2 = Get_dp(tmp+1) ;
                    else f2 = dp[tmp+1] ;
                    if(a[tmp/2] == 0) f3 = tmp/2 ;
                    else f3 = a[tmp/2] ;
                    if(tmp & 1ll)
                        ans = max( ans , res + f1 + f3 ) ;
                    else
                        ans = max(ans , res + f2 + f3 ) ;
                    res += f3 ;
                    tmp /= 2 ;
                }
                printf("%lld\n",ans ) ;
            }
            else
            {
                LONG xx ;
                LONG tmp ;
                LONG ret = 0 ;
                scanf("%lld%lld",&u,&xx) ;
                ret = 0 ;
                if(dp[u] == 0)
                    dp[u] = Get_dp(u) ;
                if(a[u] !=0) tmp = a[u] ;
                else tmp = u;
                dp[u] += ( xx - tmp ) ;
                a[u] = xx ;
                tmp = u/2;
                LONG f1,f2,f3 ;
                LONG temp ;
                while(tmp)
                {
                    if(a[tmp] !=0) f1 = a[tmp] ;
                    else f1 = tmp ;
                    if(dp[tmp<<1] != 0)
                        f2 = dp[tmp<<1] ;
                    else
                        f2 = Get_dp(tmp<<1) ;
                    if(dp[tmp<<1|1] != 0) f3 = dp[tmp<<1|1] ;
                    else
                        f3 = Get_dp(tmp<<1|1) ;
                    dp[tmp] = f1 + max( f2 , f3 ) ;
                    tmp/= 2;
                }
            }
        }
        a.clear() ;
        dp.clear() ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值