hdu 6161Big binary tree(hash+dp)

题目链接

Big binary tree

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 250 Accepted Submission(s): 77

Problem Description

You are given a complete binary tree with n nodes. The root node is numbered 1, and node x's father node is ⌊x/2⌋. At the beginning, node x has a value of exactly x. We define the value of a path as the sum of all nodes it passes(including two ends, or one if the path only has one node). Now there are two kinds of operations:

  1. change u x Set node u's value as x(1≤u≤n;1≤x≤10^10)
  2. query u Query the max value of all paths which passes node u.

Input
There are multiple cases.
For each case:
The first line contains two integers n,m(1≤n≤108,1≤m≤105), which represent the size of the tree and the number of operations, respectively.
Then m lines follows. Each line is an operation with syntax described above.

Output
For each query operation, output an integer in one line, indicating the max value of all paths which passes the specific node.

Sample Input
6 13
query 1
query 2
query 3
query 4
query 5
query 6
change 6 1
query 1
query 2
query 3
query 4
query 5
query 6

Sample Output
17
17
17
16
17
17
12
12
12
11
12
12

Source
2017 Multi-University Training Contest - Team 9

题意:给出 \(n(1 \leq n \leq 10^8)\) 个结点的完全二叉树,结点以1开始编号,结点 \(x\) 的父结点为 \(\left \lfloor \frac{x}{2} \right \rfloor\) ,初始时每个结点的权值为自己的编号,定义结点 \(x\) 的路径为树上经过结点 \(x\) 的所有路径的最长值,给出 \(m(1 \leq m \leq 10^5)\) 组询问:

  1. query x:输出树上经过结点 \(x\) 的最长路径长度
  2. change x v:将结点 \(x\) 的值变为 \(v\) .

题解:记 \(f[x]\) 为从结点 \(x\) 向下走得到的最大点权和,使用map记录,当更新一个结点 \(x\) 的权值时就往上更新 \(x\) 的所有祖先的 \(f\) 值,计算一个结点 \(x\) 向下走可得的最大权值时,若map中有键值为 \(x\) 的值,则直接返回,否则暴力向下走。query x时利用\(f\) 数组每次向上走更新答案即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;

int n,m;
map<int,ll> f,val;

ll cal(int x)  //计算从x往下走最大权值之和
{
    if(x>n) return 0;
    if(f.count(x)) return f[x];
    //f中查找不到键值为x的值说明结点x的子树中都未被更新过,暴力向下取最大
    ll ans=0;
    int t=x;
    while(x<=n) ans+=1ll*x,x=x*2+1;//一直走右子树的情况
    
    ll tmp=0;  //可能结点x的为结点n的祖先,不过一直走右子树不能经过结点n
    int t1=n;
    while(t1>0)
    {
        tmp+=1ll*t1;
        if(t1==t)
        {
            ans=max(ans,tmp);
            break;
        }
        t1/=2;
    }
    return ans;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        val.clear(),f.clear();
        while(m--)
        {
            char s[10];
            scanf("%s",s);
            int x;
            ll v;
            scanf("%d",&x);
            if(s[0]=='q')
            {
                ll ans=cal(x*2)+cal(x*2+1);
                ans+=val.count(x)? val[x]:x;
                ll tmp=cal(x);
                int rt=x%2;
                x/=2;
                while(x>=1)   //向上询问路径最长值
                {
                    tmp+=val.count(x)? val[x]:x;
                    if(rt) ans=max(ans,tmp+cal(x*2));
                    else ans=max(ans,tmp+cal(x*2+1));
                    rt=x%2;
                    x/=2;
                }
                printf("%lld\n",ans);
            }
            else
            {
                scanf("%lld",&v);
                val[x]=v;
                f[x]=max(cal(x*2),cal(x*2+1))+v;
                x/=2;
                while(x>=1)  //向上更新结点x的所有祖先的f值
                {
                    f[x]=max(cal(x*2),cal(x*2+1));
                    f[x]+=val.count(x)? val[x]:x;
                    x/=2;
                }
            }
        }
    }
    return 0;
}



posted @ 2017-08-23 15:47  tarjan's  阅读(23)  评论(0)    收藏  举报