2018 杭电多校2 - Naive Operations
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$$$
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.
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
1
2
4
4
6
看到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[]。
线段树写的有点烂。。。勉强过了
#include<stdio.h>
#include <cstring>
#define N_max 100005
typedef long long LL;
#define min(a,b) ((a)<(b)?(a):(b))
int lg[N_max << ], rg[N_max << ]
, low[N_max << ]//还要加几次
, sum[N_max << ]//已经加几次
, b[N_max << ]//bi
,ans[N_max<<]//区间的和
; #define lid(x) (x<<1)
#define rid(x) (x<<1|1)
void build(int id,int cl,int cr)
{
lg[id] = cl;
rg[id] = cr;
if (cl == cr)//build的时候完成输入,并初始化low为bi
{
scanf("%d", b + id);
low[id] = b[id]; return;
}
int left = lid(id),right=rid(id),cm= (cl + cr) >> ;
build(left, cl, cm);
build(right, cm + , cr);
low[id] = min(low[left], low[right]);//结点记录最小的low
} //low为0时,向下更新,直接处理整个区间
void addup(int id,int v)
{
sum[id] += v;
low[id] -= v;
if(lg[id]==rg[id])//遇到叶子结点
{
ans[id] += (sum[id]/ b[id]);
low[id] =b[id]- (sum[id] % b[id]);
sum[id] %=b[id] ;
}
if(low[id]<=)//low<=0说明区间内的某个数发生变化,需要继续向下更新
{
addup(lid(id), sum[id]);
addup(rid(id), sum[id]);
sum[id] = ;
ans[id] = ans[lid(id)] + ans[rid(id)];
low[id]=min(low[lid(id)], low[rid(id)]);
}
} inline void pushdown(int id)
{
if (sum[id])
{
sum[lid(id)] += sum[id];
low[lid(id)] -= sum[id];
sum[rid(id)] += sum[id];
low[rid(id)] -= sum[id];
sum[id] = ;
}
}
void add(int id,int cl,int cr,int v)
{
if (lg[id] == rg[id])//叶子结点
{
low[id]--; sum[id]++;//当进入叶子结点时,sum的值其实就是ai
ans[id] += (sum[id] / b[id]);//ai中有几个bi,ans就应该加几
low[id] = b[id] - (sum[id] % b[id]);//low变为bi-ai%bi
sum[id] %= b[id];//ai变为ai%bi
return ;
}
int left = lid(id), right = rid(id);
if(lg[id]==cl&&rg[id]==cr)//遇到匹配的区间
{
low[id]--; sum[id]++;
if(low[id]>)return ;
if ( low[id]<=)//如果low<=0,说明应该往下更新
{
//下面的更新一定是整个结点的,调用addup而不是add
addup(left, sum[id]);
addup(right, sum[id]);
//回溯时更新low和ans
low[id] = min(low[left], low[right]);
ans[id] = ans[left] + ans[right];
//因为sum已经加到子区间上了,把sum改为0
sum[id] =;
return ;
}
} //匹配区间结点
int mid = (lg[id] + rg[id]) >> ;
pushdown(id);
if (cr <= mid)
{
add(left, cl, cr, );
ans[id] = ans[left] + ans[right];
low[id] = min(low[left], low[right]);
return;
}
else if (cl > mid)
{
add(right, cl, cr, );
ans[id] = ans[left] + ans[right];
low[id] = min(low[left], low[right]);
return;
}
else
{
add(left, cl, mid,);
add(right, mid + , cr,);
ans[id] = ans[left] + ans[right];
low[id] = min(low[left], low[right]);
return;
}
} int qry(int id,int cl,int cr)
{
if (lg[id] == cl&&rg[id] == cr)
{
return ans[id];
} int mid = (lg[id] + rg[id]) >> ;
pushdown(id);
if (cr <= mid)
{
return qry(lid(id), cl, cr);
}
else if (cl > mid)
{
return qry(rid(id), cl, cr) ;
}
else
{
return qry(lid(id), cl, mid)+qry(rid(id), mid + , cr);
}
} int n,q;
char str[];
int main() { while (~scanf("%d %d" , &n,&q))
{
memset(lg, , sizeof lg);
memset(rg, , sizeof rg);
memset(low, , sizeof low);
memset(sum, , sizeof sum);
memset(ans, , sizeof ans);
build(, ,n);
int a1, a2;
for(int i=;i<q;++i)
{
scanf("%s", str);
scanf("%d %d", &a1, &a2);
if (str[] == 'a')
add(, a1, a2,);
else if (str[] == 'q')
printf("%d\n",qry(, a1, a2));
}
}
return ;
}
2018 杭电多校2 - Naive Operations的更多相关文章
- hdu6312 2018杭电多校第二场 1004 D Game 博弈
Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 2018 杭电多校3 - M.Walking Plan
题目链接 Problem Description There are $$$n$$$ intersections in Bytetown, connected with $$$m$$$ one way ...
- 2018 杭电多校1 - Chiaki Sequence Revisited
题目链接 Problem Description Chiaki is interested in an infinite sequence $$$a_1,a_2,a_3,...,$$$ which i ...
- 2018 杭电多校1 - Distinct Values
题目链接 Problem Description Chiaki has an array of n positive integers. You are told some facts about t ...
- 2018杭电多校第二场1003(DFS,欧拉回路)
#include<bits/stdc++.h>using namespace std;int n,m;int x,y;int num,cnt;int degree[100007],vis[ ...
- 2018杭电多校第六场1009(DFS,思维)
#include<bits/stdc++.h>using namespace std;int a[100010];char s[20];int zhiren[100010];vector& ...
- 2018杭电多校第五场1002(暴力DFS【数位】,剪枝)
//never use translation#include<bits/stdc++.h>using namespace std;int k;char a[20];//储存每个数的数值i ...
- 2018杭电多校第三场1003(状态压缩DP)
#include<bits/stdc++.h>using namespace std;const int mod =1e9+7;int dp[1<<10];int cnt[1& ...
- 可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)
以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作. 而后面再做有些题目,就可能会碰到一种回退的操作.这里的回退是指回到未做各种操作之 ...
随机推荐
- 【blockly教程】第六章 Blockly的进阶
6.1 模块化程序设计 一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能.所有的高级语言中都有子程序这个概念,用子程序实现模块的功能.比如在C语言中,子程序的作用是由函数完成 ...
- Typeahead的使用总结
Typeahead是Bootstrap的自动补全JS插件. 最近项目中用到,总结一下. 与autocomplish类似,通过ajax实现,实现流程是前台输入关键字,后台根据关键字查询出信息,构造jso ...
- Myeclipse - 问题集 - specified vm install not found
In Eclipse, click the ant file -- Run As -- External Tools Configuration and click on the JRE tab. S ...
- selenium--driver.switchTo()
在自动化测试中,会遇到多窗口.多iframe.多alert的情况.此时,会使用driver.switchTo()来解决. 下面时关于driver.switchTo()的详细介绍: 1.多windows ...
- cookie的介绍和自动化中cookie的操作
1 cookie是什么? cookie: 1. Cookie是一小段的文本信息:格式:python中的字典(键值对组成) 2. Cookie产生:客户端请求服务器,如果服务器需要记录该用户状态,就向客 ...
- flume-kafka-storm-hdfs-hadoop-hbase
# bigdata-demo 项目地址: https://github.com/windwant/bigdata-demo.git hadoop: hadoop hdfs操作 log输出到flume ...
- Spring Cloud(十):服务网关 Zuul(路由)【Finchley 版】
Spring Cloud(十):服务网关 Zuul(路由)[Finchley 版] 发表于 2018-04-23 | 更新于 2018-05-09 | 通过之前几篇 Spring Cloud 中 ...
- lintcode112 删除排序链表中的重复元素
删除排序链表中的重复元素 给定一个排序链表,删除所有重复的元素每个元素只留下一个. 您在真实的面试中是否遇到过这个题? Yes 样例 给出 1->1->2->null,返回 1- ...
- 372. Delete Node in a Linked List【LintCode java】
Description Implement an algorithm to delete a node in the middle of a singly linked list, given onl ...
- pymsql报错:UnicodeEncodeError: 'latin-1' codec can't encode characters End,OK!!
UnicodeEncodeError: 'latin-1' codec can't encode characters的做法基本一致,后来发现是因为使用的是mysqldb,照着网上的方法修改配置应该可 ...