关键字:线段树

归航return:(Trivial)LeetCode 1354——多次求和构造目标数组​zhuanlan.zhihu.com

归航return:(Trivial) LeetCode 84—柱状图中最大的矩形​zhuanlan.zhihu.com

Problem

力扣决定给一个刷题团队发 LeetCoin 作为奖励。同时,为了监控给大家发了多少 LeetCoin,力扣有时候也会进行查询。

该刷题团队的管理模式可以用一棵树表示:

  1. 团队只有一个负责人,编号为 1。除了该负责人外,每个人有且仅有一个领导(负责人没有领导);
  2. 不存在循环管理的情况,如 A 管理 B,B 管理 C,C 管理 A。

力扣想进行的操作有以下三种:

  1. 给团队的一个成员(也可以是负责人)发一定数量的 LeetCoin
  2. 给团队的一个成员(也可以是负责人),以及他/她管理的所有人(即他/她的下属、他/她下属的下属,……),发一定数量的LeetCoin
  3. 查询某一个成员(也可以是负责人),以及他/她管理的所有人被发到的 LeetCoin 之和。

输入:

  1. N表示团队成员的个数(编号为1~N,负责人为1);
  2. leadership是大小为(N - 1) * 2的二维数组,其中每个元素[a, b]代表ba的下属;
  3. operations 是一个长度为Q的二维数组,代表以时间排序的操作,格式如下:

operations[i][0] = 1: 代表第一种操作,operations[i][1] 代表成员的编号,operations[i][2] 代表 LeetCoin 的数量;

operations[i][0] = 2: 代表第二种操作,operations[i][1]代表成员的编号,operations[i][2] 代表 LeetCoin 的数量;

operations[i][0] = 3: 代表第三种操作,operations[i][1] 代表成员的编号;

输出:

返回一个数组,数组里是每次查询的返回值(发 LeetCoin 的操作不需要任何返回值)。由于发的 LeetCoin 很多,请把每次查询的结果模 1e9+7 (1000000007)

示例 1:

输入:N = 6, leadership = [[1, 2], [1, 6], [2, 3], [2, 5], [1, 4]], operations = [[1, 1, 500], [2, 2, 50], [3, 1], [2, 6, 15], [3, 1]]
输出:[650, 665]
解释:团队的管理关系见下图。
第一次查询时,每个成员得到的LeetCoin的数量分别为(按编号顺序):500, 50, 50, 0, 50, 0;
第二次查询时,每个成员得到的LeetCoin的数量分别为(按编号顺序):500, 50, 50, 0, 50, 15.

限制:

  1. 1 <= N <= 50000
  2. 1 <= Q <= 50000
  3. operations[i][0] != 3 时,1 <= operations[i][2] <= 5000

LCP 05. 发 LeetCoin - 力扣(LeetCode)​leetcode-cn.com

Solution

这里的问题就是区间修改,单点查询,区间查询,这类问题的正确解答,就是线段树。但是我们还需要正确地确定一个节点和其后续的所有节点的正确编号,保证节点和其后代在线段树的下标都是连续的。这个问题的解决方案就是利用树的遍历的先序遍历来进行求解。为此,我们需要维护一个 dfs 时间戳,当我们第一次访问到这个节点的时候,就对应的是节点的开始时间,完成先序遍历的递归算法完毕返回之后的时间戳,就刚好是节点的对应时间。这种算法称作 DFS 序

C++ 风格的伪代码如下:

void dfs(int idx, int& timeStamp){from[idx] = timeStamp;for (int next : adj[idx]){++timeStamp;dfs(next, timeStamp);}to[idx] = timeStamp;
}

当然,这里需要考虑下标问题,因此实际的实现中,我是将所有的坐标都减去 1,这样就可以映射到

中,使用一个大小为 n 的数组即可。在这么处理之后,操作 1 对应的就是在

[from[people], from[people]] 单点修改,2 对应的就是 [from[people], to[people]] 上区间修改,操作 3 对应的就是在 [from[people], to[people]] 上区间查询。起始时间点设定为 0,这样可以方便处理后续线段树的代码问题。

上述 test case 中,from 和 to 数组就是:

from: [0,1,2,5,3,4]to: [5,3,2,5,3,4]

这么做之前,我们还需要维护一个临接表,用于这个 dfs 操作:

vector<vector<int>> generateAdj(const vector<vector<int>>& leadership, int n){vector<vector<int>> adj(n);for (const auto & x : leadership){int src = x[0] - 1;int dst = x[1] - 1;adj[src].emplace_back(dst);}return adj;
}

注意,线段树的底层原理我并不是很懂,所以不会详细解释线段树的代码原理,而是当作黑箱子直接使用。代码如下:

class Solution {private int[] from;private int[] to;private List<Integer>[] adj;private int cnt;private static final int mod = (int)(1e9 + 7);public int[] bonus(int n, int[][] leadership, int[][] operations) {from = new int[n];to = new int[n];adj = new List[n];cnt = 0;int people = 0;int money = 0;for (int i = 0; i < n; ++i){adj[i] = new ArrayList<>();}for (int[] x : leadership){int src = x[0] - 1;int dst = x[1] - 1;adj[src].add(dst);}dfs(0);List<Integer> ans = new ArrayList<>();SegmentTree segmentTree = new SegmentTree(n);for (int[] operation : operations){switch (operation[0]){case (1):{people = operation[1] - 1;money = operation[2];segmentTree.update(from[people], from[people], money);break;}case (2):{people = operation[1] - 1;money = operation[2];segmentTree.update(from[people], to[people], money);break;}case (3):{people = operation[1] - 1;ans.add((int)(segmentTree.query(from[people], to[people]) % mod));break;}}}return ans.stream().mapToInt(i -> i).toArray();}private void dfs(int idx){from[idx] = cnt;for (int next : adj[idx]){++cnt;dfs(next);}to[idx] = cnt;}
}
class SegmentTree{private static class SegmentTreeSupport{private SegmentTreeSupport(){}public static int getLeftSon(int x){return x << 1;}public static int getRightSon(int x){return (x << 1) + 1;}public static int getFather(int x){if (x == 0){throw new IllegalArgumentException();}return x >> 1;}public final static int SEGMENT_TREE_FACTOR = 4;}private long[] tree, lazy;private final int length;private void rangeCheck(int index){if (index < 0 || index >= length){throw new IndexOutOfBoundsException(index);}}public SegmentTree(final int[] arr){if (arr == null || arr.length == 0){throw new IllegalArgumentException();}length = arr.length;tree = new long[SegmentTreeSupport.SEGMENT_TREE_FACTOR * length];lazy = new long[SegmentTreeSupport.SEGMENT_TREE_FACTOR * length];buildTree(arr, 0, length - 1, 1);}public SegmentTree(int n){this(new int[n]);}private void buildTree(final int[] arr, int fromIndex, int toIndex, int treeIndex){if (fromIndex == toIndex){tree[treeIndex] = arr[fromIndex];return;}int midIndex = (fromIndex + toIndex) >>> 1;buildTree(arr, fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex));buildTree(arr, midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex));collectSubMsgTo(treeIndex);}private void collectSubMsgTo(int treeIndex){//this line should be replaced by the function we want to maintain//that is: tree[treeIndex] = f(tree[leftIndex], tree[rightIndex])//if we want to maintain the range sumtree[treeIndex] = tree[SegmentTreeSupport.getLeftSon(treeIndex)] + tree[SegmentTreeSupport.getRightSon(treeIndex)];//if we want to maintain the minimum value
//        tree[treeIndex] = Math.min(tree[SegmentTreeSupport.getLeftSon(treeIndex)], tree[SegmentTreeSupport.getRightSon(treeIndex)]);//if we want to maintain the maximum value
//        tree[treeIndex] = Math.max(tree[SegmentTreeSupport.getLeftSon(treeIndex)], tree[SegmentTreeSupport.getRightSon(treeIndex)]);}private void getLazyStatus(int fromIndex, int toIndex, int treeIndex, long diff){//this line should be replaced by the function we want to maintain//now, we set the value to diff
//        lazy[treeIndex] = diff;
//        tree[treeIndex] = (long)(toIndex - fromIndex + 1) * diff;//if we want to add the value by diff, then code is:tree[treeIndex] += diff * (toIndex - fromIndex + 1);lazy[treeIndex] += diff;//if we want to set the value and maintain the minimum/maximum value
//        tree[treeIndex] = diff;
//        lazy[treeIndex] = diff;//if we want to add the value and maintain the minimum/maximum value
//        tree[treeIndex] += diff;
//        lazy[treeIndex] += diff;}private void push_down(int fromIndex, int toIndex, int treeIndex){int midIndex = (fromIndex + toIndex) >>> 1;getLazyStatus(fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex), lazy[treeIndex]);getLazyStatus(midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex), lazy[treeIndex]);lazy[treeIndex] = 0;}public void update(int updateFromIndex, int updateToIndex, long diff){rangeCheck(updateFromIndex);rangeCheck(updateToIndex);update(updateFromIndex, updateToIndex, 0, length - 1, 1, diff);}private void update(int updateFromIndex, int updateToIndex, int fromIndex, int toIndex, int treeIndex, long diff){if (updateFromIndex <= fromIndex && toIndex <= updateToIndex){getLazyStatus(fromIndex, toIndex, treeIndex, diff);return;}if (lazy[treeIndex] != 0){push_down(fromIndex, toIndex, treeIndex);}int midIndex = (fromIndex + toIndex) >>> 1;if (updateFromIndex <= midIndex){update(updateFromIndex, updateToIndex, fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex), diff);}if (updateToIndex >= midIndex + 1){update(updateFromIndex, updateToIndex, midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex), diff);}collectSubMsgTo(treeIndex);}public long query(int queryFromIndex, int queryToIndex){rangeCheck(queryFromIndex);rangeCheck(queryToIndex);if (queryFromIndex > queryToIndex){throw new IllegalArgumentException();}return query(queryFromIndex, queryToIndex, 0, length - 1, 1);}private long query(int queryFromIndex, int queryToIndex, int fromIndex, int toIndex, int treeIndex){if (queryFromIndex <= fromIndex && toIndex <= queryToIndex){return tree[treeIndex];}int midIndex = (fromIndex + toIndex) >>> 1;if (lazy[treeIndex] != 0){push_down(fromIndex, toIndex, treeIndex);}//if we want to maintain the range sumreturn (queryFromIndex <= midIndex ?query(queryFromIndex, queryToIndex, fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex)): 0)+ (queryToIndex >= midIndex + 1? query(queryFromIndex, queryToIndex, midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex)) :0);//if we want to maintain the minimum value
//        return Math.min(
//                (queryFromIndex <= midIndex ? query(queryFromIndex, queryToIndex, fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex)) : Long.MAX_VALUE),
//                (queryToIndex >= midIndex + 1 ? query(queryFromIndex, queryToIndex, midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex)) : Long.MAX_VALUE)
//        );//if we want to maintain the maximum value
//        return Math.max(
//                (queryFromIndex <= midIndex ? query(queryFromIndex, queryToIndex, fromIndex, midIndex, SegmentTreeSupport.getLeftSon(treeIndex)) : Long.MIN_VALUE),
//                (queryToIndex >= midIndex + 1 ? query(queryFromIndex, queryToIndex, midIndex + 1, toIndex, SegmentTreeSupport.getRightSon(treeIndex)) : Long.MIN_VALUE)
//        );}@Overridepublic String toString() {return "SegmentTreen" +"tree = " + Arrays.toString(tree) +", lazy = " + Arrays.toString(lazy);}
}

时间复杂度上,建立线段树和 DFS 的过程:

,每次查询的时间复杂度:
,总共有
次查询,因此总的时间复杂度是:

空间复杂度上,线段树加上 lazy 标记需要 8 倍的空间,fromto 是 1 倍的空间,因此是

EOF。

freeswitch 发update sip消息_LeetCode LCP 05——发 LeetCoin相关推荐

  1. freeswitch 发update sip消息_【PDA】SIP中生物学确认

    Biological Qualification 生物学确认的定义在之前的文章中推送过,大家可以直接点击蓝色字体链接部分,生物学确认不是对所有的SIP都适用,仅针对声明了无菌的SIP过程适用. 为了进 ...

  2. freeswitch 发update sip消息_VOS修改SIP注册端口

    VOS2009-2120 修改配置文件地址 /usr/kunshi/mbx2009/etc/softswitch.conf VOS3000-2140 修改配置文件地址 /home/kunshi/mbx ...

  3. LCP 05 发 LeetCoin

    力扣决定给一个刷题团队发LeetCoin作为奖励.同时,为了监控给大家发了多少LeetCoin,力扣有时候也会进行查询. 该刷题团队的管理模式可以用一棵树表示: 团队只有一个负责人,编号为1.除了该负 ...

  4. 发微信模版消息换行用\n

    发微信模版消息换行用\n 发微信模版消息换行用\n 发微信模版消息换行用\n 发微信模版消息换行用\n 发微信模版消息换行用\n 转载于:https://blog.51cto.com/xuqin/19 ...

  5. Linphone android去电增加自定义SIP消息头的流程分析

    一.首先看一下如何在发起去电的sip请求中添加自定义的消息头 增加自定义头消息发方法,so已经提供了native方法, 发起呼叫的示例如下: LinphoneCallParams params = l ...

  6. sip消息概念(一)

    SIP 也是类似 HTTP 的一个协议集合,在网上搜索了一下相关的信息,摘录如下: SIP消息的第一行包含消息的类型和所使用的SIP版本(2.0).在请求中,这一行还包含一个叫做SIP URI的地址. ...

  7. sip消息类型和消息代码详解-转

    在学习asterisk的时候,经常遇到一些远程服务器传回的代码,这些代码都有很重要的信息,让我们了解到对方的sip是如何响应我们这边的sip消息的,于是网上找到了这些sip消息类型和消息代码,自己收藏 ...

  8. JAVA对接公众号(二、处理微信服务器发来的消息)

    一.验证公众号配置的服务器信息. 须知:处理微信服务器发来的消息之前必须先通过公众号配置的服务器验证 获取AccessToken,里面的HttpClientUtil类可以从我csdn资源中找 /*** ...

  9. SIP消息格式详解(基于XML消息体)

    SIP消息分为请求和响应,格式由一个请求行/状态行.几个标题头.一个空行和一个消息体(可选)组成,之间使用回车换行符表示终结,即使消息中未包含消息体,空行也不能省略: 1.请求行/状态行 <1& ...

最新文章

  1. 超级干货丨优美的课程笔记,吴恩达点赞的深度学习课程信息图
  2. 格式工厂mac_干货|格式搞得定,论文不用愁
  3. 【计算机网络】数据链路层 : 广域网 ( 广域网概念 | PPP 协议 | PPP 协议功能 | PPP 协议组成 | PPP 协议帧格式 )
  4. 《数据科学:R语言实现》——2.5 使用Excel文件
  5. 1万条数据大概占多大空间_「数据分析」Sqlserver的窗口函数的精彩应用之数据差距与数据岛...
  6. exp导出excel oracle_OracleToExcel_Oracle导出excel数据(OracleToExcel)下载 v3.1 官方版 - 121下载站...
  7. python获取windows系统信息_Python获取Windows系统信息
  8. ImportError: libnvinfer.so.7: cannot open shared object file: No such file or directory
  9. 基于AE+C#实现在TOCControl中实现指定图层删除
  10. Integer的自动装箱底层缓存原理
  11. 【SSH网上商城项目实战28】使用Ajax技术局部更新商品数量和总价
  12. 【新农合专题】新农合系统资料汇总贴(新增134个)
  13. vmware 虚拟机安装系统成功,没有虚拟网卡的完美解决方法
  14. 利用Python进行股票交易分析(一):量化交易策略——买点与卖点的量化
  15. java程序设计课后答案 刘慧宁_【单选题】建筑立面图中,室外地坪轮廓线应用( )。...
  16. 展现量、点击量、点击率;访客数、访问次数、浏览量的区别与作用
  17. 三坐标检测之精密零件测量的恒温时间
  18. Java算法:LeetCode算法Java版合集1111-1588题
  19. 科普:指纹识别的工作原理
  20. 罗永浩曾经深刻地改变过这个世界

热门文章

  1. HTML+CSS+JS面试题(附带答案)
  2. python的魔法_python魔法方法大全
  3. 小米10pro第二个摄像头下面_小米10至尊纪念版、小米10 Pro对比评测:至尊版“至尊”在哪里?...
  4. 李宏毅《机器学习》作业班+带打比赛
  5. 滴滴 KDD 2018 论文详解:基于强化学习技术的智能派单模型
  6. 消息摘要算法HmacMD5的实现
  7. 国科大高级人工智能9-模糊数学和遗传算法
  8. 总结Vue中index.html、main.js、App.vue、index.js之间关系以及Vue项目加载流程
  9. nginx 高并发优化参数
  10. WCF系列(一)BasicHttpBinding 和 WsHttpBinding 的不同点