CDOJ 841 权值线段树

来源:互联网 发布:原生js修改css样式 编辑:程序博客网 时间:2024/05/22 14:25

休生伤杜景死惊开

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

陆伯言军陷八卦阵之中,分明只是一条直路,却怎的也走不到尽头。阵中尽是石堆,以某一石堆为参考,无论向走还是向右,总是会回到出发的石堆,最后幸得一黄姓老翁带路才得脱出。

陆伯言逃离八卦阵后,来到山顶观察此阵,记从左往右第i堆石堆的高度为AiAi,发现任何两堆较矮的石堆都能和它们之间的一座较高的石堆形成"八卦锁",将其中之人牢牢锁住,无从逃脱。

根据石堆的情况,陆伯言大致计算了“八卦锁”的数量(即 Ai<Aj>Ak,i<j<kAi<Aj>Ak,i<j<k 的组合数),不禁心中一惊,对孔明惊为天人,遂放弃追击,收兵回吴。

“有劳岳父了。” “为何将其放走?” “...一表人才,何必浪费于此。”

Input

第一行一个整数nn,表示石堆堆数。

接下来一行,nn个整数,第ii个数表示从左到右第ii堆石堆的高度AiAi

1n50000,1Ai327681≤n≤50000,1≤Ai≤32768

Output

一个整数,“八阵锁”的数目。

Sample input and output

Sample InputSample Output
51 2 3 4 1
6
题目大意:
显然,这道题目要求统计数列中类似“小——大——小”这样形式的数对共有多少。
解题思路:
因此,我们只要求出对于每一个数,左边比它小的树德个数ln[i],以及右边比它小的数的个数rn[i]。将ln[i]和rn[i]相乘加起来就是答案。
维护ln与rn数组,我们将运用权值线段树
维护一个[0,32768]的线段树(见数据范围),将1~32768中的数的出现次数压入线段树。
如果询问比k小的数有多少个,求区间[1,k-1]的和(这也是为什么从0开始建树)。
因为要求ln和rn,建两棵树或初始化(博主为前者)
依次询问每个数,求出ln和rn,并维护答案。
#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>using namespace std;#define LL long longconst int N=1000010;const int MAX=1e9+7;int n,k;struct Q{char c;int a;}q[N];int a[N],len;int ln[N],rn[N];inline void R(int &v){v=0;char ch=getchar();int f=0;while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}while(isdigit(ch)){v=(v<<3)+(v<<1)+ch-'0';ch=getchar();}if(f) v=-v;}namespace ib {char b[100];}inline void P(int x){    if(x==0) {putchar(48); return;}    if(x<0) {putchar('-'); x=-x;}    char *s=ib::b;    while(x) *(++s)=x%10, x/=10;    while(s!=ib::b) putchar((*(s--))+48);}struct Segtree{struct trie{int l,r,len;int sum;int lz;}tree[N<<2];void updata(int o)//更新 {tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;}void build(int o,int l,int r)//建树 {tree[o].sum=0;tree[o].l=l;tree[o].r=r;tree[o].len=r-l+1;if(l==r) {tree[o].sum=0;return;}int mid=(l+r)>>1;build(o<<1,l,mid);build(o<<1|1,mid+1,r);updata(o);}int query(int o,int ql,int qr)//区间和 {int l=tree[o].l,r=tree[o].r;if(ql<=l&&qr>=r) return tree[o].sum;int res=0;int mid=l+r>>1;if(qr<=mid) res+=query(o<<1,ql,qr);if(ql>mid) res+=query(o<<1|1,ql,qr);if(ql<=mid&&qr>mid){res+=query(o<<1,ql,mid)+query(o<<1|1,mid+1,qr);}return res;}void change(int o,int q,int v)//单点加{int l=tree[o].l,r=tree[o].r;if(l==r) {tree[o].sum+=v;return;}int mid=l+r>>1;if(q<=mid) change(o<<1,q,v);else change(o<<1|1,q,v);updata(o);}}A,B;int main(){//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);int i,j;while(scanf("%d",&n)!=EOF){for(i=1;i<=n;i++) scanf("%d",&a[i]);A.build(1,0,32768);B.build(1,0,32768);for(i=1;i<=n;i++){ln[i]=A.query(1,0,a[i]-1);A.change(1,a[i],1);}for(i=n;i>=1;i--){rn[i]=B.query(1,0,a[i]-1);B.change(1,a[i],1);}LL ans=0;for(i=1;i<=n;i++)ans+=1ll*ln[i]*rn[i];cout<<ans<<endl;}return 0;}

结语:
*权值线段树
线段树基础题型,权值的思想有点难想到,但知道以后线段树很简单。




1 0