cdoj841-休生伤杜景死惊开 (逆序数变形)【线段树 树状数组】

http://acm.uestc.edu.cn/#/problem/show/841

休生伤杜景死惊开

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

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

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

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

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

Input

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

接下来一行,n个整数,第i个数表示从左到右第i堆石堆的高度Ai。

1≤n≤50000,1≤Ai≤32768

Output

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

Sample input and output

Sample InputSample Output
5
1 2 3 4 1
6

 

 

 

 

题意:求Ai<Aj>Ak,i<j<k 的组合数。

思路:这道题目其实是求逆序数,稍作变形,可以采用线段树或者树状数组来实现。两次扫描,先从前往后扫,即插即查,每插入一次,便计算该位置之前的总数并记录,再从后往前扫,原理相同。最后求对应位置乘积和。详情见代码:

线段树实现:

 1 #include <fstream>
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 
 6 using namespace std;
 7 
 8 const int N=50002;
 9 int n,m,maxn,a[N],l[N];
10 struct node
11 {
12     int left,right;
13     int sum_;
14 }tree[4*32770];
15 
16 void build(int id,int l,int r);//建一棵线段树
17 int query_sum(int id,int l,int r);//查询区间和
18 void update(int id,int pos);//更新位置pos的值增加1
19 
20 int main()
21 {
22     //freopen("D:\\input.in","r",stdin);
23     //freopen("D:\\output.out","w",stdout);
24     long long ans=0;
25     scanf("%d",&n);
26     for(int i=1;i<=n;i++)
27         scanf("%d",&a[i]),maxn=max(a[i],maxn);
28     build(1,1,maxn);
29     for(int i=1;i<=n;i++)
30     {
31         update(1,a[i]);
32         l[i]=query_sum(1,1,a[i]-1);
33     }
34     build(1,1,maxn);
35     for(int i=n;i>=1;i--)
36     {
37         update(1,a[i]);
38         ans+=l[i]*query_sum(1,1,a[i]-1);
39     }
40     printf("%lld\n",ans);
41     return 0;
42 }
43 void build(int id,int l,int r)
44 {
45     tree[id].left=l;
46     tree[id].right=r;
47     if(l==r)
48     {
49         tree[id].sum_=0;
50     }
51     else
52     {
53         int mid=(l+r)/2;
54         build(2*id,l,mid);
55         build(2*id+1,mid+1,r);
56         tree[id].sum_=tree[2*id].sum_+tree[2*id+1].sum_;
57     }
58 }
59 int query_sum(int id,int l,int r)
60 {
61     if(l>r) return 0;//注意参数的大小关系限制
62     if(tree[id].left==l&&tree[id].right==r)
63         return tree[id].sum_;
64     else
65     {
66         int mid=(tree[id].left+tree[id].right)/2;
67         if(r<=mid)  return query_sum(2*id,l,r);
68         else if(l>mid)  return query_sum(2*id+1,l,r);
69         else
70             return query_sum(2*id,l,mid)+query_sum(2*id+1,mid+1,r);
71     }
72 }
73 void update(int id,int pos)
74 {
75     if(tree[id].left==tree[id].right)
76     {
77         tree[id].sum_++;
78     }
79     else
80     {
81         int mid=(tree[id].left+tree[id].right)/2;
82         if(pos<=mid)    update(2*id,pos);
83         else update(2*id+1,pos);
84         tree[id].sum_=tree[2*id].sum_+tree[2*id+1].sum_;
85     }
86 }
View Code

树状数组实现:

 1 #include <fstream>
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 
 6 using namespace std;
 7 
 8 int n,m,maxn;
 9 int a[50002],tree[32770],l[50002];
10 
11 int read(int pos);//求 sum[1,pos]的答案
12 void update(int pos);//把a[pos]加上1
13 
14 int main()
15 {
16     //freopen("D:\\input.in","r",stdin);
17     //freopen("D:\\output.out","w",stdout);
18     long long ans=0;
19     scanf("%d",&n);
20     for(int i=1;i<=n;i++)
21         scanf("%d",&a[i]),maxn=max(a[i],maxn);
22     for(int i=1;i<=n;i++)
23     {
24         update(a[i]);
25         l[i]=read(a[i]-1);
26     }
27     memset(tree,0,sizeof(tree));
28     for(int i=n;i>=1;i--)
29     {
30         update(a[i]);
31         ans+=l[i]*read(a[i]-1);
32     }
33     printf("%lld\n",ans);
34     return 0;
35 }
36 int read(int pos)
37 {
38     int ans=0;
39     while(pos>0)
40     {
41         ans+=tree[pos];
42         pos-=pos&(-pos);
43     }
44     return ans;
45 }
46 void update(int pos)
47 {
48     while(pos<=maxn)
49     {
50         tree[pos]++;
51         pos+=pos&(-pos);
52     }
53 }
View Code
posted @ 2015-01-18 01:39  jiu~  阅读(648)  评论(4编辑  收藏  举报