HZAU_1203_One Stroke(尺取+倍增)

来源:互联网 发布:成为java架构师 编辑:程序博客网 时间:2024/06/06 02:42

原题:

1203: One Stroke

Time Limit: 2 Sec  Memory Limit: 1280 MB

Description


    There is a complete binary tree which includes n nodes. Each node on the tree has a weight w, each edge on the tree is directed from the parent node to the child node. Give you a pen, draw from the any node along the directed edge at one stroke. It is required that the sum of those drawn nodes’ s weight is no more than k. How many node can be drawn at most in one stroke?

Input


    The first line input an positive integer T(1<=T<=10)indicates the number of test cases. Next, each case occupies two lines. The first line input two positive integers n(1<=n<=10^6) and k,(1<=k<=10^9)

    The second line input n integers w(1<=w <=10^3),, indicate the weight of nodes from the first level of the tree and from left to right.

Output


    For each test cases, output one line with the most number of nodes can be drawn in one stroke. If any node likes this doesn’t exists, output -1.
Sample Input

1
5 6
2 3 4 1 7

Sample Output


3

题意

给你一颗完全二叉树每个节点都有一个权值,然后要你从上往下找一条链,值得链上权值的和<K,且节点数最大。

思路

dfs+尺取法,从根节点开始从上往下找权值和不超过k的最大结点数。找到后此时下面增加一个结点,上面减少一个节点,看是否满足小于k,如不满足,则上面再减少一个结点。直到小于k,将此时的结点数与上一个满足小于k的状态比较,选取较大者。(基于二叉树先序遍历)
用倍增法存储祖先们的结点,可以提高效率。

涉及知识及算法

尺取法:有这么一类问题,需要在给的一组数据中找到不大于某一个上限的“最优连续子序列”。于是就有了这样一种方法,找这个子序列的过程很像毛毛虫爬行方式,比较流行的叫法是“尺取法”。

倍增法:倍增法与二分法都是快速查找的方法。二分法已知目标所在区间,在区间内依次减半查找目标。倍增法未知目标所在区间,依次倍增以便快速找到目标区间。

代码

#include <bits/stdc++.h>#define inf 100000000#define met(a,b) memset(a,b,sizeof a)#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1typedef long long ll;using namespace std;const int N = 1e6+5;const int M = 4e5+5;int n,m,ans;int a[N];                 //a数组存储点权(点值)int sum[N],fa[N][22],dep[N];void dfs(int u,int f){           //u为第几个结点,f为u父亲结点    if(u>n)return;               //如果所有结点遍历完毕,则跳出    sum[u]=sum[f]+a[u];         //sum存储路过的点权和    dep[u]=dep[f]+1;             //dep数组记录二叉树深度    fa[u][0]=f;             //存放父亲结点序号    for(int i=1;i<22;i++)fa[u][i]=fa[fa[u][i-1]][i-1];//(倍增法)△fa[i][j]表示i结点的第2^j祖先,1倍祖先就是父亲,2倍祖先是父亲的父亲,    if(sum[u]<=m)ans=max(ans,dep[u]);       //选取最大结点数//(接上行)4倍祖先就是父亲的父亲的父亲的父亲......。    else {        int v=u;        for(int i=21;i>=0;i--){            if(sum[u]-sum[fa[v][i]]<=m){              //快速查找满足条件的值所在区间                ans=max(ans,dep[u]-dep[fa[v][i]]);       //选择较多的点                v=fa[v][i];                     //△此处用意:逐级向上找父亲精确查找目标值            }        }    }    dfs(u*2,u);    dfs(u*2+1,u);}int main() {    int T,x,y,v;    scanf("%d",&T);    for(int cas=1;cas<=T;cas++){        met(fa,0);        ans=0;        met(sum,0);        met(dep,0);        scanf("%d%d",&n,&m);        for(int i=1;i<=n;i++) scanf("%d",&a[i]);        dfs(1,0);        printf("%d\n",ans==0?-1:ans);    }    return 0;} /**************************************************************    Problem: 1203    Language: C++    Result: Accepted    Time:1291 ms    Memory:99160 kb****************************************************************/

代码转载自博客园博主贱人方,附上链接http://www.cnblogs.com/jianrenfang/p/6754569.html向他表示感谢。

csdn博主我在浪里的注释给了我很大帮助,附上链接http://blog.csdn.net/qq_20200047/article/details/70861098向他表示感谢。

原创粉丝点击