cdoj841-休生伤杜景死惊开 (逆序数变形)【线段树 树状数组】
http://acm.uestc.edu.cn/#/problem/show/841
休生傷杜景死驚開
Time Limit: 3000/1000MS (Java/Others) ??? Memory Limit: 65535/65535KB (Java/Others)
Submit?Status陸伯言軍陷八卦陣之中,分明只是一條直路,卻怎的也走不到盡頭。陣中盡是石堆,以某一石堆為參考,無論向走還是向右,總是會回到出發的石堆,最后幸得一黃姓老翁帶路才得脫出。
陸伯言逃離八卦陣后,來到山頂觀察此陣,記從左往右第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
| 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轉載于:https://www.cnblogs.com/jiu0821/p/4231391.html
總結
以上是生活随笔為你收集整理的cdoj841-休生伤杜景死惊开 (逆序数变形)【线段树 树状数组】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网站添加到IIS和附件进程调试(新手使用
- 下一篇: 在数据库中分析sql执行性能