CDQ分治題目題解-------luoguP2345 [USACO04OPEN]MooFest G題解
阿新 • • 發佈:2020-08-20
題目連結:(https://www.luogu.com.cn/problem/P2345)
這道題大多數人的解法是根據v來進行排序,而我則是用的排序x的方法,看見還沒人發就來發一篇.(這道題資料真滴水啊)
解題步驟
首先我們可以看到,題目中給的是許多奶牛的座標和聽力值,我們很容易聯想到把其中中一個元素進行排序,這樣子就可以只用考慮其中的一個元素了 然而我解這道題的時候聯絡到了三值偏序問題,(將a作為第一關鍵字排序後再用歸併排鬚排序b值的時候無論怎麼變換,在範圍[l,mid]裡面的a值都會小於範圍[mid+1,r]中的a值,然後就是騷操作來了),所以我就排序了奶牛的座標
接著我又利用歸併排鬚,排序v的同時計算出答案
具體解題細節
首先:一定要記得開longlong
答案的計算公式
解釋一下:大家應該都知道歸併排序的原理,就是兩個序列進行合併。而在此演算法中第一個序列的“x”數值一定小於任意一個第二個序列的“x”數值
rt記錄的就是目前已經被合併的第二序列的數的“x”數值之和
lt記錄的就是目前已經被合併的第一序列的數的“x”數值之和
t1表示的就是目前到了序列一的第幾個位置了,t1-l就是當前節點減去起點就是已經合併的序列1的數的個數
然後t2表示的就是目前到了序列2的第幾個位置了,t2-mid-1就是當前節點減去起點就是已經合併的序列2的數的個數
1.res+=T[t1].v*(rt-(t2-mid-1)*T[t1].x),t1++;//如果當前節點是歸併排序中屬於序列1合併來的,那麼它的x一定比已經合併的屬於序列2的數的x值小, // 對於任意的序列1中的“x”數值一定小於序列二中的數值,所以任意序列2中的“x”數值減去序列1中的 “x”數值一定大於0, // 同時又因為序列2中的每一個數都要減去當前這個合併進去的數的“x”數值,// 所以直接用目前已經被合併的第二序列的數的“x”數值之和減去已經合併的序列2的數的個數與當前點的“x”數值的乘積就行了 2.res+=T[t2].v*((t1-l)*T[t2].x-lt),t2++;//如果是當前節點在歸併排序中屬於序列2合併來的,那麼它的x一定比前面的大,上面解釋的很詳細,不贅述了。
(還有就是我在此處通過一點點的推斷可以去除絕對值,具體見答案計算以及上面提到的“在範圍[l,mid]裡面的a值都會小於範圍[mid+1,r]中的a值”)
CODE
#include <bits/stdc++.h> using namespace std; #define int long long struct node{int v,x;}T[20005],b[20005]; int n,res=0; int cmp(node A,node B){ if(A.x != B.x)return A.x<B.x; return A.v<B.v; } int CDQ(int l , int r){ if(l == r)return 0; int mid=(l+r)>>1; CDQ(l,mid);CDQ(mid+1,r); int t1=l,t2=mid+1,lt=0,rt=0; for (int i = l ; i <= r ; i ++){ if(T[t1].v <= T[t2].v && t1 <= mid || t2 > r ) b[i]=T[t1],lt+=T[t1].x,//合併序列的同時計算答案 res+=T[t1].v*(rt-(t2-mid-1)*T[t1].x),t1++; else b[i]=T[t2],rt+=T[t2].x, res+=T[t2].v*((t1-l)*T[t2].x-lt),t2++;; } for (int i = l ; i <= r ; i ++)T[i]=b[i]; return 0; } signed main(){ cin>>n; for (int i = 1 ; i <= n ; i ++) cin>>T[i].v>>T[i].x; sort(T+1,T+1+n,cmp); CDQ(1,n); cout<<res; return 0; }