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)
以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作. 而后面再做有些题目,就可能会碰到一种回退的操作.这里的回退是指回到未做各种操作之 ...
随机推荐
- Java设计模式(5)——创建型模式之建造者模式(Builder)
一.概述 概念 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.(与工厂类不同的是它用于创建复合对象) UML图 主要角色 抽象建造者(Builder)——规范建造方法与结果 ...
- ARP级ping命令:arping
一.工作原理 地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议,是网络链路层的协议,在局域网中使用.主机发送信息时将包 ...
- C#正则表达式提取HTML中IMG标签的SRC地址(转)
一般来说一个 HTML 文档有很多标签,比如“<html>”.“<body>”.“<table>”等,想把文档中的 img 标签提取出来并不是一件容易的事.由于 i ...
- Uber优步北京第一组奖励政策
优步北京第一组: 定义为2015年6月1日凌晨前(不含6月1日)激活的司机(以优步后台数据显示为准) 滴滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机 ...
- 1 多任务fork Unix/Linux/Mac
# 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以 1.如下程序,来模拟“唱歌跳舞”这件事情 #-*- coding:utf-8 -*- import time de ...
- Java:内存泄露和内存溢出
1. 内存溢出 (Memory Overflow) 是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory:比如申请了一个integer,但给它存了long才能存下的数,那就 ...
- android stadio 编译报错:download fastutil-7.2.0.jar
在Ubuntu上面,新安装的stadio,第一次编译项目的时候, 一直开在下载 fastutil-7.2.0.jar 原因是需要FQ.那么改一下你的buil.gradle buildscript { ...
- 利尔达仿真器加有人CC3200模块USR-C322上电测试
1. 使用利尔达的CC3200底板仿真器对有人CC3200模块USR-C322进行烧写,测试. 2. 连接的接口,需要连接6根线,如下,供电测试,第一波测试,输入+++回复a,然后在输入a,返回+OK ...
- 探索 Flask
探索 Flask 探索 Flask 是一本关于使用 Flask 开发 Web 应用程序的最佳实践和模式的书籍.这本书是由 426 名赞助人 在 Kickstarter 上 于 2013 年 7 月资助 ...
- 了解和分析iOS Crash
WeTest 导读 北京时间凌晨一点,苹果一年一度的发布会如期而至.新机型的发布又会让适配相关的同学忙上一阵子啦,并且iOS Crash的问题始终伴随着移动开发者.本文将从三个阶段,由浅入深的介绍如何 ...