POJ 3928 线段树 单点更新+区间查询
来源:互联网 发布:美工队长金亨泰 编辑:程序博客网 时间:2024/05/16 09:40
【题目链接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19613
【解题报告】
在《训练指南》上看到的这道题目,原题出在树状数组下,考虑到大部分树状数组可以做的题目线段树都可以做,虽然效率低一些,代码长一些,但好处在于维护更方便,扩展性也更强,所以还是用线段树来做这道题目。可能是最近手生,虽然题目很裸,仍然调了一个多小时才过掉这道题目。希望这段时间还是可以加大训练量。
简单说一下题意:
N个不同的数的一个排列,任取其中三个点a,b,c。保证c在a,b之间。问不同的方案有多少种。
如果我们直接枚举边界a和c,找a,c间有多少个b,显而易见的是,这样做的时间复杂度是O(N^2)所以这里有一个很经典的转化思想,我们枚举中间点b,找左边和右边分别有多少个比他小的点。这样做的时间复杂度是O(nlogn)为什么呢?我们先来求,对a[i],它左边有多少个数比它小。我们建立一颗线段树,数据范围直接开1..100000。维护线段信息tree[i]为第i个节点代表的线段(L,R)之间已经插入了多少个数, 每得到一个a[i]把它插入到线段树里,然后维护tree数组。同时我们对a[i],查询1..a[i]-1有多少数已经插入到线段树里了。得到的信息就是pre[i]。类似的,我们再倒着进行一次插入操作,可以得到suf[i](表示a[i]后面有多少个比它大的数)。其中插入操作的时间复杂度是O(logn),查询的时间复杂度是O(logn).总的时间复杂度就是O(nlogn).
【参考代码】
#include<iostream>#include<cstring>#include<cstdio>using namespace std;typedef long long LL;const int maxn=100000+50;int N;int a[maxn];int pre[maxn],suf[maxn];int tree[maxn*4],num[4*maxn];void build( int O, int L, int R ){ if( L==R ){ tree[O]=0; num[L]=O; return ; } int mid=(L+R)/2; build( O*2, L, mid ); build( O*2+1, mid+1, R ); tree[O]=tree[O*2+1]+tree[O*2];}void ins( int O, int L, int R, int x ){ if( L>R )return; if( L==R ) { tree[O]=1; return ; } int mid=(L+R)/2; if( x<=mid )ins( O*2, L, mid, x ); else ins( O*2+1, mid+1, R, x ); tree[O]=tree[O*2+1]+tree[O*2];}int query( int O, int L, int R, int qL, int qR ){ // cout<<L<<" "<<R<<endl; if ( L>R ) return 0; if( L==R )return tree[O]; if( qL>R || qR<L ) return 0; if( qL<=L && R<=qR )return tree[O]; int mid=(L+R)/2; if( qR<=mid )return query( O*2, L, mid, qL, qR ); if( mid<qL ) return query( O*2+1, mid+1, R, qL, qR ); return query( O*2, L,mid, qL, qR )+ query( O*2+1, mid+1, R, qL, qR );}int main(){ int T; cin>>T; while( T-- ) { scanf( "%d",&N ); for( int i=1; i<=N; i++ ) scanf( "%d",&a[i] ); build( 1,1,maxn ); for( int i=1; i<=N; i++ ) { ins( 1,1,maxn, a[i] ); pre[i]=query( 1, 1,maxn, 1, a[i]-1 ); //初始范围是1..maxn,要查找1..a[i]-1之间有几个数在a[i]左边 } build( 1,1,maxn ); for( int i=N; i>=1; i-- ) { ins( 1,1,maxn, a[i] ); suf[i]=query( 1,1,maxn, a[i]+1, maxn ); } LL ans=0; for( int i=2; i<N; i++ ) { ans+=(LL)suf[i]*pre[i]; ans+=(LL)(i-1-pre[i])*( N-i-suf[i] ); } printf( "%I64d\n", ans ); } return 0;}
0 0
- POJ 3928 线段树 单点更新+区间查询
- POJ 3264 Balanced Lineup (线段树单点更新 区间查询)
- zoj (单点更新区间查询:线段树)
- 线段树单点更新和区间查询
- hdu1166(线段树单点更新区间查询)
- hdu5316 Magician (线段树+单点更新+区间查询+区间合并)
- [模板]线段树的建树、查询、单点更新、区间更新
- POJ 3264-Balanced Lineup(线段树:单点更新,区间查询)
- !POJ 2352 左下角星星-线段树-(单点更新,区间查询)
- POJ 题目2892 Tunnel Warfare(线段树单点更新查询,求单点所在最大连续区间长度)
- POJ 2828 线段树 单点更新,单点查询
- 线段树(堆式)[单点更新, 区间查询]
- hdoj 4339 线段树 单点更新,区间查询
- HDU 2795 线段树(单点更新 区间查询)
- HDOJ3016Man Down(线段树(区间更新,单点查询)+DP)
- HDU1754线段树单点更新区间查询(数组版)
- Necklace (线段树单点更新+区间查询+离线操作)
- HDU 4819:单点更新,区间查询的二维线段树
- Day16,33天的一半。
- 那一天的京城大雪纷飞
- 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPoo!并讲解两者的区别和游戏中的用途!
- 指数型母函数 简介——Tanky Woo
- USACO 3.4 American Heritage美国血统 (树的遍历)
- POJ 3928 线段树 单点更新+区间查询
- 我眼中的操作系统中断
- 【Android游戏开发之九】(细节处理)触屏事件中的Bug解决方案以及禁止横屏和竖屏切换!
- hanoi(汉诺塔)问题
- 插入法排序
- 健康的生活——导言篇
- 黑马程序员——单例设计模式
- HDU 3364 (高斯消元)
- 应用提交 App Store 上架被拒的原因都有哪些