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 Input | Sample 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 }
树状数组实现:
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 }