[均摊 线段树] UOJ#228. 基础数据结构练习题
来源:互联网 发布:java web 高并发 编辑:程序博客网 时间:2024/06/05 09:19
题意
题解
先膜一下九老师 %%%
可以先感性理解一下,不断进行区间开根号操作,整个序列会不断“趋近于一致“。比如说: 3123 42 5 --> 55 6 2 --> 7 2 1 --> 2 1 1 --> 1 1 1
就算数字之间相差很大,搞几下就很接近了。
由于有区间加操作,所以可以想到从相邻数的差进行考虑。
区间加操作体现到差分数组上只是两个点的改变。
所以就有这样一种想法,不断的区间开根号使数与数之间的差减小,当一段数全部相等时开根号就变成了区间加了,而区间加是可以打标记的。如果不能打标记就直接递归下去。
但是这里容易忽略一种情况:9 8 -> 3 2
。
可以发现当
现在我们得到了一个算法,直接呈现代码更直观:
void Sqrt(int p,int L,int R){ //开根号操作 if(R<seg[p].L||seg[p].R<L) return; if(L<=seg[p].L&&seg[p].R<=R){ if(seg[p]._min==seg[p]._max||seg[p]._max-seg[p]._min==(LL)sqrt(seg[p]._max)-(LL)sqrt(seg[p]._min)){ //如果满足可以打标记的条件 LL tem=(LL)sqrt(seg[p]._max)-seg[p]._max; seg[p].add_plus(tem); //打区间加的标记 return; } } pushdown(p); Sqrt(p<<1,L,R); Sqrt(p<<1|1,L,R); maintain(p);}
这样能通过全部数据,而且跑得很快。
这样的复杂度为什么科学呢?
设题目ai范围为V。考虑差分数字,两个V范围的数开个几次就差不多差值就没有了,而区间加操作体现到差分数组上只是两个点的改变,也就m次把差值增加V。
设计势能函数为所有相邻数差值和,一开始势能
现在有一个问题,V开几次根号就变成1呢?我们可以粗略估算一下,设V开k次根号后变为2:
一个点,给他开一次根号需要logn的复杂度(从根走到叶)。所以用
总复杂度大概是
下面是完整代码。
#include<cstdio>#include<cmath>#include<algorithm>using namespace std;const int maxn=100005;typedef long long LL;inline int getint(){ char ch=getchar(); int res=0,ff=1; while(!('0'<=ch&&ch<='9')){ if(ch=='-') ff=-1; ch=getchar(); } while('0'<=ch&&ch<='9') res=res*10+ch-'0', ch=getchar(); return res*ff;}struct node{ int L,R; LL sum,_max,_min,tag; void add_plus(LL val){ sum+=val*(R-L+1), _max+=val; _min+=val; tag+=val; }} seg[maxn*4];void pushdown(int p){ if(seg[p].tag){ seg[p<<1].add_plus(seg[p].tag); seg[p<<1|1].add_plus(seg[p].tag); seg[p].tag=0; }}void maintain(int p){ seg[p].sum=seg[p<<1].sum+seg[p<<1|1].sum; seg[p]._min=min(seg[p<<1]._min,seg[p<<1|1]._min); seg[p]._max=max(seg[p<<1]._max,seg[p<<1|1]._max);}void build(int p,int L,int R){ seg[p].L=L; seg[p].R=R; if(L==R){ seg[p].sum=seg[p]._max=seg[p]._min=getint(); return; } int mid=(L+R)>>1; build(p<<1,L,mid); build(p<<1|1,mid+1,R); maintain(p);}void Updata(int p,int L,int R,int val){ if(R<seg[p].L||seg[p].R<L) return; if(L<=seg[p].L&&seg[p].R<=R){ seg[p].add_plus(val); return; } pushdown(p); Updata(p<<1,L,R,val); Updata(p<<1|1,L,R,val); maintain(p);}void Sqrt(int p,int L,int R){ if(R<seg[p].L||seg[p].R<L) return; if(L<=seg[p].L&&seg[p].R<=R){ if(seg[p]._min==seg[p]._max||seg[p]._max-seg[p]._min==(LL)sqrt(seg[p]._max)-(LL)sqrt(seg[p]._min)){ LL tem=(LL)sqrt(seg[p]._max)-seg[p]._max; seg[p].add_plus(tem); return; } } pushdown(p); Sqrt(p<<1,L,R); Sqrt(p<<1|1,L,R); maintain(p);}LL Query(int p,int L,int R){ if(R<seg[p].L||seg[p].R<L) return 0; if(L<=seg[p].L&&seg[p].R<=R) return seg[p].sum; pushdown(p); return Query(p<<1,L,R)+Query(p<<1|1,L,R);}int n,Q;int main(){ freopen("uoj228.in","r",stdin); freopen("uoj228.out","w",stdout); scanf("%d%d",&n,&Q); build(1,1,n); while(Q--){ int pd=getint(),x=getint(),y=getint(); if(pd==1){ int z=getint(); Updata(1,x,y,z); } else if(pd==2) Sqrt(1,x,y); else if(pd==3) printf("%lld\n",Query(1,x,y)); } return 0;}
- [均摊 线段树] UOJ #228. 基础数据结构练习题
- [均摊 线段树] UOJ#228. 基础数据结构练习题
- 【线段树+均摊思想】UOJ #228 基础数据结构练习题
- uoj#228. 基础数据结构练习题
- UOJ#228 基础数据结构练习题
- [UOJ#228]基础数据结构练习题
- UOJ 228 基础数据结构练习题
- 【UOJ #228】 基础数据结构练习题
- UOJ#228——基础数据结构练习题
- 【UOJ228】基础数据结构练习题(线段树)
- [线段树 均摊复杂度] BZOJ 2130 魔塔
- [BZOJ2130][均摊复杂度线段树]魔塔
- UOJ #228(基础数据结构练习题-区间开根+区间加+区间求和)
- [均摊 平衡树 || 线段树] HDU 5634 Rikka with Phi
- 【数据结构基础]】数据库练习题
- UOJ 164 线段树历史最值
- [bzoj4574][UOJ#196][ZJOI2016]线段树
- 线段树练习题一
- http编程系列(二)——java爬虫实现刷个人博客的访问量
- 182
- POJ-3126 Prime Path ( BFS )
- Spring Ehcache 整合
- BT 4.2 蓝牙工具之工欲善其事必先利其器
- [均摊 线段树] UOJ#228. 基础数据结构练习题
- Spring AOP环绕通知小例子
- JDBC_数据库连接池
- 错题本系统
- 指针数组和数组指针
- C#学习笔记(三)—–C#高级特性中的委托与事件:关于事件
- 索引
- hibernate入门
- JavaScript-Html-绘制时钟