Codeforces 1136E - Nastya Hasn't Written a Legend - [线段树+二分]
题目链接:https://codeforces.com/problemset/problem/1136/E
题意:
给出一个 $a[1 \sim n]$,以及一个 $k[1 \sim (n-1)]$,初始保证所有的 $1 \le i \le n-1$ 都满足 $a[i]+k[i] \le a[i+1]$。
现在有两种操作:
第一种是令指定的 $a[i]$ 加上一个非负整数 $x$,此时若有 $a[i] + k[i] > a[i+1]$,则 $a[i+1]$ 变为 $a[i] + k[i]$,往后依次类推。
第二种是给定一个区间 $[l,r]$ 求 $a[l] + \cdots + a[r]$。
题解:
首先,我们知道对一个 $a[i]$ 加上 $x$ 后,根据其后面的 $a[i] + k[i] \le a[i+1], a[i+1] + k[i+1] \le a[i+2], \cdots$ 的“松紧程度”的变化,$a[i]$ 加上 $x$ 其带来的影响会逐步减弱,直到在某个位置 $j$ 之后完全消失,这个 $a[j]$ 是最后一个要被修改的数。
那么,如果我们找到了这个区间 $[i,j]$,我们现在要做的修改操作,就是要对这个区间进行一定的修改。
不难发现,这个区间内的第一个数变成了 $a[i]+x$,第二个变成了 $a[i]+x+k[i]$,第三个变成了 $a[i]+x+k[i]+k[i+1]$,依次类推……
考虑这个式子可以分为两部分:$a[i] + x$ 部分,以及 $k[i] + k[i+1] + \cdots$ 部分。
可以考虑分开维护这两个部分,前一部分很好维护,线段树区间更新;后一部分直接维护比较困难,我们可以这样维护:
$k[i],k[i]+k[i+1],k[i]+k[i+1]+k[i+2],\cdots$,不难看出是一个类似于 $k$ 数组的前缀和的求和,举个栗子:
$k_3 ,\: k_3+k_4 ,\: k_3+k_4+k_5 ,\: k_3+k_4+k_5+k_6$,如果是前缀和求和,那么应当是 $k_1+k_2+k_3 \:,\: k_1+k_2+k_3+k_4 \:,\: k_1+k_2+k_3+k_4+k_5 \:,\: k_1+k_2+k_3+k_4+k_5+k_6$。
也就是说,要在前缀和上减掉 $4 \times (k_1+k_2)$,不难发现,这个值是比较好维护的,是对某一段区间直接赋值,所以可以用线段树维护这个东西。
AC代码:
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const ll INF=1e18;
- const int maxn=1e5+;
- int n,q;
- ll a[maxn],k[maxn],s[maxn];
- struct Node
- {
- int l,r;
- ll val,lazy;
- void update(ll x)
- {
- val=(r-l+)*x;
- lazy=x;
- }
- };
- struct SegmentTree
- {
- #define ls (rt<<1)
- #define rs (rt<<1|1)
- Node o[maxn<<];
- void pushdown(int rt)
- {
- if(o[rt].lazy!=-INF)
- {
- o[ls].update(o[rt].lazy);
- o[rs].update(o[rt].lazy);
- o[rt].lazy=-INF;
- }
- }
- void pushup(int rt)
- {
- o[rt].val=o[ls].val+o[rs].val;
- }
- void build(int rt,int l,int r,ll v[])
- {
- o[rt].l=l, o[rt].r=r;
- o[rt].lazy=-INF;
- if(l==r)
- {
- o[rt].val=v[l];
- return;
- }
- int mid=(l+r)>>;
- build(ls,l,mid,v);
- build(rs,mid+,r,v);
- pushup(rt);
- }
- void update(int rt,int st,int ed,ll val)
- {
- if(st<=o[rt].l && o[rt].r<=ed)
- {
- o[rt].update(val);
- return;
- }
- pushdown(rt);
- int mid=(o[rt].l+o[rt].r)>>;
- if(st<=mid) update(ls,st,ed,val);
- if(mid<ed) update(rs,st,ed,val);
- pushup(rt);
- }
- ll query(int rt,int st,int ed)
- {
- if(st<=o[rt].l && o[rt].r<=ed) return o[rt].val;
- pushdown(rt);
- ll res=;
- int mid=(o[rt].l+o[rt].r)>>;
- if(st<=mid) res+=query(ls,st,ed);
- if(mid<ed) res+=query(rs,st,ed);
- return res;
- }
- }T[];
- inline ll getval(int p)
- {
- return T[].query(,p,p)+k[p]-T[].query(,p,p);
- }
- void modify(int p,ll x)
- {
- int l=p, r=n;
- ll base=getval(p);
- while(l<r)
- {
- int mid=(l+r+)>>;
- if(base+x+k[mid]-k[p]>getval(mid)) l=mid;
- else r=mid-;
- }
- T[].update(,p,r,base+x);
- T[].update(,p,r,k[p]);
- }
- int main()
- {
- ios::sync_with_stdio();
- cin.tie(), cout.tie();
- cin>>n;
- for(int i=;i<=n;i++) cin>>a[i];
- k[]=s[]=;
- for(int i=;i<=n;i++) cin>>k[i], k[i]+=k[i-], s[i]=s[i-]+k[i];
- T[].build(,,n,a);
- T[].build(,,n,k);
- cin>>q;
- char op[];
- while(q--)
- {
- cin>>op;
- if(op[]=='+')
- {
- int p; ll x; cin>>p>>x;
- modify(p,x);
- }
- if(op[]=='s')
- {
- int l,r; cin>>l>>r;
- ll res1=T[].query(,l,r);
- ll res2=s[r]-s[l-]-T[].query(,l,r);
- cout<<res1+res2<<'\n';
- }
- }
- }
有一个注意点是懒标记初始化成负无穷。
Codeforces 1136E - Nastya Hasn't Written a Legend - [线段树+二分]的更多相关文章
- Codeforces 1136E Nastya Hasn't Written a Legend 线段树
vp的时候没码出来.. 我们用set去维护, 每一块区域, 每块区域内的元素与下一个元素的差值刚好为ki,每次加值的时候我们暴力合并, 可以发现我们最多合并O(n)次. 然后写个线段树就没了. #in ...
- Codeforces 1136E Nastya Hasn't Written a Legend (线段树教做人系列)
题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i].有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质.比如a[i]加上x之后, ...
- codeforces#1136E. Nastya Hasn't Written a Legend(二分+线段树)
题目链接: http://codeforces.com/contest/1136/problem/E 题意: 初始有a数组和k数组 有两种操作,一,求l到r的区间和,二,$a_i\pm x$ 并且会有 ...
- Codeforces Gym 100803G Flipping Parentheses 线段树+二分
Flipping Parentheses 题目连接: http://codeforces.com/gym/100803/attachments Description A string consist ...
- Codeforces Gym 100231B Intervals 线段树+二分+贪心
Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...
- [Codeforces 464E] The Classic Problem(可持久化线段树)
[Codeforces 464E] The Classic Problem(可持久化线段树) 题面 给出一个带权无向图,每条边的边权是\(2^{x_i}(x_i<10^5)\),求s到t的最短路 ...
- Educational Codeforces Round 64 (Rated for Div. 2) (线段树二分)
题目:http://codeforces.com/contest/1156/problem/E 题意:给你1-n n个数,然后求有多少个区间[l,r] 满足 a[l]+a[r]=max([l, ...
- Codeforces 671C - Ultimate Weirdness of an Array(线段树维护+找性质)
Codeforces 题目传送门 & 洛谷题目传送门 *2800 的 DS,不过还是被我自己想出来了 u1s1 这个 D1C 比某些 D1D 不知道难到什么地方去了 首先碰到这类问题我们肯定考 ...
- Codeforces Round #244 (Div. 2) B. Prison Transfer 线段树rmq
B. Prison Transfer Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/problemset/pro ...
随机推荐
- 十进制转为x进制的递归代码
十进制转为x进制的递归代码 #include <stdio.h> void fun(int n,int x) { ) return; else { fun(n/x,x); printf(& ...
- iOS实现pdf文件预览,上下翻页、缩放,读取pdf目录
最近有个朋友想做一个pdf预览,要求能够上下滑动翻页.带缩放.目录跳转功能. 因为之前我只做过简单的预览,那时直接用uiwebview实现的,这次找了下资料,发现一个比较好的库. 其原理实现: 自定义 ...
- 浅谈 CSS 预处理器: 为什么要使用预处理器?
CSS 自诞生以来,基本语法和核心机制一直没有本质上的变化,它的发展几乎全是表现力层面上的提升.最开始 CSS 在网页中的作用只是辅助性的装饰,轻便易学是最大的需求:然而如今网站的复杂度已经不可同日而 ...
- mac 下 通过 brew 安装 MariaDB
其实在两年多前,我就推荐大家使用MariaDB了,其实真的很好用,性能高,也可以完全替代mysql 主要是这oracle实在是太**了,java都收费了,mysql迟早的事... 安装MariaDB之 ...
- vs code 设置问题
现已取消 .vue 文件与 HTML 的默认关联,需要手动配置.vue 文件里不能使用div + Tab 键快速生成 html 代码 "emmet.syntaxProfiles" ...
- C#函数的默认参数——填坑记
昨天踩了一个坑.默认参数 + 增量发布的坑. 过程是这样的. 1. 有一个底层的方法,格式形如 void Test<T>(int p1, string p2, Func<T> ...
- com.android.jack.CommandLine: Internal compiler error
Android studio编译的时候出现错误: SEVERE: com.android.jack.CommandLine: Internal compiler error Error:Executi ...
- 简单的 FastDFS + Nginx 应用实例
版权声明:本文为GitChat作者的原创文章,未经 GitChat 同意不得转载. https://blog.csdn.net/GitChat/article/details/79479148 wx_ ...
- COUNT分组条件去重的sql统计语句示例(mysql)
常规情况下的sql分组统计为: ) from 表 where 条件 group by 字段; 但是有时往往需要添加不同的条件已经去重的统计以上语句就不能满足需求. 解决方案为: 1.添加条件的统计方案 ...
- JS中常用的Math方法
1.min()和max()方法 Math.min()用于确定一组数值中的最小值.Math.max()用于确定一组数值中的最大值. alert(Math.min(2,4,3,6,3,8,0,1,3)); ...