Luogu P3644 八鄰旁的橋
阿新 • • 發佈:2022-02-15
solution
\(\quad\)首先考慮 \(k=1\) 的情況,其實抽象一下很像之前做的中位數的題,答案就是所有位置的中位數。
\(\quad\)再考慮 \(k=2\) 的情況,那麼無非就是將一條河分成兩邊,一邊走這個\(\quad\)橋,另一邊走那個橋。於是我們列舉分割點,這樣就轉換成了第一種情況。
\(\quad\)但是如何求第一種情況的路程和?
\(\quad\)我們可以想象一個數軸上,橋在一個點,那麼總距離就是橋到左邊點的總距離加上橋到右邊點的總距離,一旦決定了分割點,那麼橋就是那一塊所有座標的中位數。
\(\quad\)因為居民的位置滿足單調性,於是我們可以從左到右掃一遍預處理左邊塊的答案,再從右到左掃一遍預處理右邊塊的答案。
\(\quad\)每加入一個居民,就是加入了 \(2\) 個點,我們需要維護當前所有數的中位數和小於中位數的和,和大於中位數的和。都說了每次只加入 \(2\) 個點,也就是我們只用考慮這 \(2\) 個點影響,那就好維護多了,這裡維護的操作很明顯可以用平衡樹做,但是也可以用對頂堆做,操作的時候可以記錄比中位數小的數的個數和總和,比中位數大的數的個數和總和,或者也可以直接記錄答案。
code
#include<bits/stdc++.h> #define int long long using namespace std; const int maxn=1000010; struct node { int a,b; }p[maxn]; bool cmp(node a,node b) { return a.a+a.b<b.a+b.b; } int k,n,ans; int x1,x2; char c1,c2; int tot,cnt; int s1,s2; int sum[2][10000010],p1[1000010]; priority_queue<int ,vector<int> ,less<int> > q1; priority_queue<int ,vector<int> ,greater<int> > q2; signed main() { cin>>k>>n; for(int i=1;i<=n;i++) { cin>>c1>>x1>>c2>>x2; if(c1==c2) ans+=abs(x2-x1); else { ans++; p[++tot].a=x1; p[tot].b=x2; p1[++cnt]=x1; p1[++cnt]=x2; } } if(k==1) { sort(p1+1,p1+1+cnt); int pos=p1[cnt/2]; for(int i=1;i<=cnt;i++) { ans+=abs(p1[i]-pos); } cout<<ans<<endl; } else { sort(p+1,p+1+tot,cmp); for(int i=1;i<=tot;i++) { q1.push(p[i].a); q1.push(p[i].b); s1+=p[i].a+p[i].b; s2+=q1.top(); s1-=q1.top(); q2.push(q1.top()); q1.pop(); if(q1.top()>q2.top()) { int num1=q2.top(); int num2=q1.top(); q1.pop(); q2.pop(); q2.push(num2); q1.push(num1); s1+=num1-num2; s2-=num1-num2; } sum[0][i]=s2-s1; } while(q1.size()) q1.pop(); while(q2.size()) q2.pop(); s1=s2=0; for(int i=tot;i>=1;i--) { q1.push(p[i].a); q1.push(p[i].b); s1+=p[i].a+p[i].b; s2+=q1.top(); s1-=q1.top(); q2.push(q1.top()); q1.pop(); if(q1.top()>q2.top()) { int num1=q2.top(); int num2=q1.top(); q1.pop(); q2.pop(); q2.push(num2); q1.push(num1); s1+=num1-num2; s2-=num1-num2; } sum[1][i]=s2-s1; } long long anss=1e18; for(int i=1;i<=tot+1;i++) { anss=min(anss,sum[0][i-1]+sum[1][i]); } cout<<ans+anss; } return 0; }