1. 程式人生 > 其它 >TZOJ1614 MooFest詳解(樹狀陣列)

TZOJ1614 MooFest詳解(樹狀陣列)

具體題目請點TZOJ1614

題目簡述

  有N頭牛,它們在同一行上且位於不同的位置,求每兩頭牛產生值的總和。

題解

  暴力的話雙重迴圈O(n^2),當然會T了。此時我們就得找別的方法。通過觀察我們可以發現Vi越大這頭牛的貢獻就越大,所以我們可以按照Vi從大到小,逐個計算,每計算一個放走一頭牛。我們可以證明

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
 4 typedef long long
ll; 5 const ll N=2e4+7; 6 const ll INF=1e18; 7 int n; 8 ll num1,num2; //得到右邊和左邊牛的個數 9 ll dist[N]; //樹狀陣列用來記錄牛座標 10 int flag[N]; //樹狀陣列用來記錄牛的頭數 11 struct F{ 12 ll val; //牛的權值 13 ll pos; //牛在的位置 14 int tt; //用來對映到樹狀陣列中的第一位置 15 }zt[N]; 16 bool cmp(const F &a,const F &b){ //以牛在座標的位置從小到大排序
17 return a.pos<b.pos; 18 } 19 bool cmp2(const F &a,const F &b){ //以牛的權值從大到小排序 20 return a.val>b.val; 21 } 22 int lowbit(int x){ 23 return x&(-x); 24 } 25 void update(int k,int pos,int x){ //常規更新操作 26 while(pos<=n){ 27 dist[pos]+=k; 28 flag[pos]+=x;
29 pos+=lowbit(pos); 30 } 31 } 32 ll get(int pos){ //查詢牛的右邊(實際上是整個隊伍) 33 ll res=0; 34 while(pos>0){ 35 res+=dist[pos]; 36 num1+=flag[pos]; 37 pos-=lowbit(pos); 38 } 39 return res; 40 } 41 ll get2(int pos){ //查詢牛的左邊 42 ll res=0; 43 while(pos>0){ 44 res+=dist[pos]; 45 num2+=flag[pos]; 46 pos-=lowbit(pos); 47 } 48 return res; 49 } 50 void sovle(){ 51 cin>>n; 52 for(int i=1;i<=n;i++){ 53 cin>>zt[i].val>>zt[i].pos; 54 } 55 sort(zt+1,zt+1+n,cmp); 56 for(int i=1;i<=n;i++){ 57 zt[i].tt=i; 58 update(zt[i].pos,i,1); 59 } 60 sort(zt+1,zt+1+n,cmp2); 61 ll res=0; 62 for(int i=1;i<n;i++){ 63 num1=0;num2=0; 64 ll x=get(n),y=get2(zt[i].tt-1); 65 res+=(x-y-(num1-num2)*zt[i].pos)*zt[i].val; //右邊的總值 66 res+=(num2*zt[i].pos-y)*zt[i].val; //左邊的總值 67 update(-zt[i].pos,zt[i].tt,-1); //這頭牛已經開會結束了,可以走了 68 } 69 cout<<res<<endl; 70 } 71 int main(){ 72 IOS; 73 int t=1; 74 while(t--){ 75 sovle(); 76 } 77 return 0; 78 } 79 /* 80 5 81 2 1 82 2 5 83 2 3 84 2 9 85 2 7 86 87 80 88 */
View Code