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 */