题目链接

Problem Description
In a galaxy far, far away, there are two integer sequence a and b of length n.
b is a static permutation of 1 to n. Initially a is filled with zeroes.
There are two kind of operations:
1.add l r: add one for $$$a_l, a_{l+1}...a_r$$$
2.query l r: query $$$\sum_{i=l}^{r}\lfloor a_i/b_i\rfloor$$$
Input
There are multiple test cases, please read till the end of input file.
For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries.
In the second line, n integers separated by spaces, representing permutation b.
In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation.
$$$1\le n,q\le 100000, 1\le l\le r\le n$$$, there're no more than 5 test cases.
Output
Output the answer for each 'query', each one line.
Sample Input
5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5
Sample Output
1
1
2
4
4
6
题意
$$$a_1$$$~$$$a_n$$$最开始都是0,已知$$$b_1$$$~$$$b_n$$$,一共q次操作,add操作把$$$a_l$$$~$$$a_r$$$都加1,query操作求$$$\sum_{i=l}^{r}\lfloor a_i/b_i\rfloor$$$
思路

看到add l r和query l r,就觉得这是一道线段树,可是$$$b_i$$$的存在又破坏了区间的统一性。

根据线段树的特点,为了把add和query的复杂度降低到log(n),结点应该记录这个区间被add了几次,以及区间的和$$$\sum_{i=l}^{r}\lfloor a_i/b_i\rfloor$$$,核心问题在于怎样更新每个结点的和。

由于复杂度的原因,不能每次都重新算一遍所有结点的和,但因为每次add操作都只加1,很多结点的值都不会立刻变化;实际上区间的和的变化有这样的特点:在若干次add之前,区间和都不会改变,只有当某一项$$$a_i$$$增加到$$$b_i$$$的倍数时,区间和才会相应的+1,也就是说,在整个区间被加若干次之前,都不用更新它,只把它总共被加的次数+1;一旦某一项变成$$$a_i$$$增加到$$$b_i$$$的倍数时,区间和+1,可以认为$$$a_i$$$又恢复为0,接下来整个区间又需要若干次才会改变。如果在区间和未改变时省掉更新操作,那么复杂度将一定程度上降低。

PS:题解视频里也讲啦,总更新次数很少的

考虑这样一个标签,它记录的是,整个区间至少被加几次以后,才会让其中一项发生变化。最开始的时候,$$$a_i$$$的标签都是$$$a_i$$$,父亲结点则记录两个儿子的标签较小的那个,当标签的值变为0的时候,更新区间和,并把对应的结点的标签重新设为$$$a_i$$$,并更新路径上的所有标签;而在标签大于0的时候,则不需要对区间进行更新。

具体的来说,假设用sum[]记录区间被完整的加了几次,low[]记录区间再被加几次就需要更新,ans[]记录区间和,每次add操作,找到对应的区间i,把sum[i]++,low[i]--,如果low[i]等于0了,就向下更新,把子区间的sum加上sum[i],low减去sum[i],把所有low变为小于等于0的区间继续向下更新,直到把发生变化的那个$$$a_i$$$重设为0,ans[]++,然后在回溯的时候,更新路径上的所有low[]和ans[]。

代码

线段树写的有点烂。。。勉强过了

  1. #include<stdio.h>
  2. #include <cstring>
  3. #define N_max 100005
  4. typedef long long LL;
  5. #define min(a,b) ((a)<(b)?(a):(b))
  6. int lg[N_max << ], rg[N_max << ]
  7. , low[N_max << ]//还要加几次
  8. , sum[N_max << ]//已经加几次
  9. , b[N_max << ]//bi
  10. ,ans[N_max<<]//区间的和
  11. ;
  12.  
  13. #define lid(x) (x<<1)
  14. #define rid(x) (x<<1|1)
  15. void build(int id,int cl,int cr)
  16. {
  17. lg[id] = cl;
  18. rg[id] = cr;
  19. if (cl == cr)//build的时候完成输入,并初始化low为bi
  20. {
  21. scanf("%d", b + id);
  22. low[id] = b[id]; return;
  23. }
  24. int left = lid(id),right=rid(id),cm= (cl + cr) >> ;
  25. build(left, cl, cm);
  26. build(right, cm + , cr);
  27. low[id] = min(low[left], low[right]);//结点记录最小的low
  28. }
  29.  
  30. //low为0时,向下更新,直接处理整个区间
  31. void addup(int id,int v)
  32. {
  33. sum[id] += v;
  34. low[id] -= v;
  35. if(lg[id]==rg[id])//遇到叶子结点
  36. {
  37. ans[id] += (sum[id]/ b[id]);
  38. low[id] =b[id]- (sum[id] % b[id]);
  39. sum[id] %=b[id] ;
  40. }
  41. if(low[id]<=)//low<=0说明区间内的某个数发生变化,需要继续向下更新
  42. {
  43. addup(lid(id), sum[id]);
  44. addup(rid(id), sum[id]);
  45. sum[id] = ;
  46. ans[id] = ans[lid(id)] + ans[rid(id)];
  47. low[id]=min(low[lid(id)], low[rid(id)]);
  48. }
  49. }
  50.  
  51. inline void pushdown(int id)
  52. {
  53. if (sum[id])
  54. {
  55. sum[lid(id)] += sum[id];
  56. low[lid(id)] -= sum[id];
  57. sum[rid(id)] += sum[id];
  58. low[rid(id)] -= sum[id];
  59. sum[id] = ;
  60. }
  61. }
  62. void add(int id,int cl,int cr,int v)
  63. {
  64. if (lg[id] == rg[id])//叶子结点
  65. {
  66. low[id]--; sum[id]++;//当进入叶子结点时,sum的值其实就是ai
  67. ans[id] += (sum[id] / b[id]);//ai中有几个bi,ans就应该加几
  68. low[id] = b[id] - (sum[id] % b[id]);//low变为bi-ai%bi
  69. sum[id] %= b[id];//ai变为ai%bi
  70. return ;
  71. }
  72. int left = lid(id), right = rid(id);
  73. if(lg[id]==cl&&rg[id]==cr)//遇到匹配的区间
  74. {
  75. low[id]--; sum[id]++;
  76. if(low[id]>)return ;
  77. if ( low[id]<=)//如果low<=0,说明应该往下更新
  78. {
  79. //下面的更新一定是整个结点的,调用addup而不是add
  80. addup(left, sum[id]);
  81. addup(right, sum[id]);
  82. //回溯时更新low和ans
  83. low[id] = min(low[left], low[right]);
  84. ans[id] = ans[left] + ans[right];
  85. //因为sum已经加到子区间上了,把sum改为0
  86. sum[id] =;
  87. return ;
  88. }
  89. }
  90.  
  91. //匹配区间结点
  92. int mid = (lg[id] + rg[id]) >> ;
  93. pushdown(id);
  94. if (cr <= mid)
  95. {
  96. add(left, cl, cr, );
  97. ans[id] = ans[left] + ans[right];
  98. low[id] = min(low[left], low[right]);
  99. return;
  100. }
  101. else if (cl > mid)
  102. {
  103. add(right, cl, cr, );
  104. ans[id] = ans[left] + ans[right];
  105. low[id] = min(low[left], low[right]);
  106. return;
  107. }
  108. else
  109. {
  110. add(left, cl, mid,);
  111. add(right, mid + , cr,);
  112. ans[id] = ans[left] + ans[right];
  113. low[id] = min(low[left], low[right]);
  114. return;
  115. }
  116. }
  117.  
  118. int qry(int id,int cl,int cr)
  119. {
  120. if (lg[id] == cl&&rg[id] == cr)
  121. {
  122. return ans[id];
  123. }
  124.  
  125. int mid = (lg[id] + rg[id]) >> ;
  126. pushdown(id);
  127. if (cr <= mid)
  128. {
  129. return qry(lid(id), cl, cr);
  130. }
  131. else if (cl > mid)
  132. {
  133. return qry(rid(id), cl, cr) ;
  134. }
  135. else
  136. {
  137. return qry(lid(id), cl, mid)+qry(rid(id), mid + , cr);
  138. }
  139. }
  140.  
  141. int n,q;
  142. char str[];
  143. int main() {
  144.  
  145. while (~scanf("%d %d" , &n,&q))
  146. {
  147. memset(lg, , sizeof lg);
  148. memset(rg, , sizeof rg);
  149. memset(low, , sizeof low);
  150. memset(sum, , sizeof sum);
  151. memset(ans, , sizeof ans);
  152. build(, ,n);
  153. int a1, a2;
  154. for(int i=;i<q;++i)
  155. {
  156. scanf("%s", str);
  157. scanf("%d %d", &a1, &a2);
  158. if (str[] == 'a')
  159. add(, a1, a2,);
  160. else if (str[] == 'q')
  161. printf("%d\n",qry(, a1, a2));
  162. }
  163. }
  164. return ;
  165. }

2018 杭电多校2 - Naive Operations的更多相关文章

  1. hdu6312 2018杭电多校第二场 1004 D Game 博弈

    Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  2. 2018 杭电多校3 - M.Walking Plan

    题目链接 Problem Description There are $$$n$$$ intersections in Bytetown, connected with $$$m$$$ one way ...

  3. 2018 杭电多校1 - Chiaki Sequence Revisited

    题目链接 Problem Description Chiaki is interested in an infinite sequence $$$a_1,a_2,a_3,...,$$$ which i ...

  4. 2018 杭电多校1 - Distinct Values

    题目链接 Problem Description Chiaki has an array of n positive integers. You are told some facts about t ...

  5. 2018杭电多校第二场1003(DFS,欧拉回路)

    #include<bits/stdc++.h>using namespace std;int n,m;int x,y;int num,cnt;int degree[100007],vis[ ...

  6. 2018杭电多校第六场1009(DFS,思维)

    #include<bits/stdc++.h>using namespace std;int a[100010];char s[20];int zhiren[100010];vector& ...

  7. 2018杭电多校第五场1002(暴力DFS【数位】,剪枝)

    //never use translation#include<bits/stdc++.h>using namespace std;int k;char a[20];//储存每个数的数值i ...

  8. 2018杭电多校第三场1003(状态压缩DP)

    #include<bits/stdc++.h>using namespace std;const int mod =1e9+7;int dp[1<<10];int cnt[1& ...

  9. 可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)

    以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作. 而后面再做有些题目,就可能会碰到一种回退的操作.这里的回退是指回到未做各种操作之 ...

随机推荐

  1. Java设计模式(5)——创建型模式之建造者模式(Builder)

    一.概述 概念 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.(与工厂类不同的是它用于创建复合对象) UML图   主要角色 抽象建造者(Builder)——规范建造方法与结果 ...

  2. ARP级ping命令:arping

    一.工作原理 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议,是网络链路层的协议,在局域网中使用.主机发送信息时将包 ...

  3. C#正则表达式提取HTML中IMG标签的SRC地址(转)

    一般来说一个 HTML 文档有很多标签,比如“<html>”.“<body>”.“<table>”等,想把文档中的 img 标签提取出来并不是一件容易的事.由于 i ...

  4. Uber优步北京第一组奖励政策

    优步北京第一组: 定义为2015年6月1日凌晨前(不含6月1日)激活的司机(以优步后台数据显示为准) 滴滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机 ...

  5. 1 多任务fork Unix/Linux/Mac

    # 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 1.如下程序,来模拟“唱歌跳舞”这件事情 #-*- coding:utf-8 -*- import time de ...

  6. Java:内存泄露和内存溢出

    1. 内存溢出 (Memory Overflow) 是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory:比如申请了一个integer,但给它存了long才能存下的数,那就 ...

  7. android stadio 编译报错:download fastutil-7.2.0.jar

    在Ubuntu上面,新安装的stadio,第一次编译项目的时候, 一直开在下载 fastutil-7.2.0.jar 原因是需要FQ.那么改一下你的buil.gradle buildscript { ...

  8. 利尔达仿真器加有人CC3200模块USR-C322上电测试

    1. 使用利尔达的CC3200底板仿真器对有人CC3200模块USR-C322进行烧写,测试. 2. 连接的接口,需要连接6根线,如下,供电测试,第一波测试,输入+++回复a,然后在输入a,返回+OK ...

  9. 探索 Flask

    探索 Flask 探索 Flask 是一本关于使用 Flask 开发 Web 应用程序的最佳实践和模式的书籍.这本书是由 426 名赞助人 在 Kickstarter 上 于 2013 年 7 月资助 ...

  10. 了解和分析iOS Crash

    WeTest 导读 北京时间凌晨一点,苹果一年一度的发布会如期而至.新机型的发布又会让适配相关的同学忙上一阵子啦,并且iOS Crash的问题始终伴随着移动开发者.本文将从三个阶段,由浅入深的介绍如何 ...