[bzoj4071] [Apio2015]巴鄰旁之橋
Description
一條東西走向的穆西河將巴鄰旁市一分為二,分割成了區域 A 和區域 B。
每一塊區域沿著河岸都建了恰好 1000000001 棟的建築,每條岸邊的建築都從 0 編號到 1000000000。相鄰的每對建築相隔 1 個單位距離,河的寬度也是 1 個單位長度。區域 A 中的 i 號建築物恰好與區域 B 中的 i 號建築物隔河相對。
城市中有 N 個居民。第 i 個居民的房子在區域 Pi 的 Si 號建築上,同時他的辦公室坐落在 Qi 區域的 Ti 號建築上。一個居民的房子和辦公室可能分佈在河的兩岸,這樣他就必須要搭乘船隻才能從家中去往辦公室,這種情況讓很多人都覺得不方便。為了使居民們可以開車去工作,政府決定建造不超過 K 座橫跨河流的大橋。
由於技術上的原因,每一座橋必須剛好連線河的兩岸,橋樑必須嚴格垂直於河流,並且橋與橋之間不能相交。當政府建造最多 K 座橋之後,設 Di 表示第 i 個居民此時開車從家裡到辦公室的最短距離。請幫助政府建造橋樑,使得 D1+D2+⋯+DN 最小。
Input
輸入的第一行包含兩個正整數 K 和 N,分別表示橋的上限數量和居民的數量。
接下來 N 行,每一行包含四個引數:Pi,Si,Qi 和 Ti,表示第 i 個居民的房子在區域 Pi 的 Si 號建築上,且他的辦公室位於 Qi 區域的 Ti 號建築上。
Output
輸出僅為一行,包含一個整數,表示 D1+D2+⋯+DN 的最小值。
Sample Input
1 5
B 0 A 4
B 1 B 3
A 5 B 7
B 2 A 6
B 1 A 7
Sample Output
24
HINT
\(n\leqslant 10^5,k\in \{1,2\}\)。
Solution
在同側的先預處理掉,過橋的\(1\)單位距離也預處理掉。
然後對於\(k=1\)的情況,橋顯然要建在中位數上,\(splay\)維護下。
然後對於\(k=2\)的情況,顯然有個斷點,斷點前的線段走第一座橋,斷點後的線段走第二座橋,所以列舉斷點,\(splay\)維護即可。
注意如果沒有在河兩側的特判掉。
#include<bits/stdc++.h> using namespace std; #define int long long void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; } void print(int x) { if(x<0) x=-x,putchar('-'); if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);puts("");} const int maxn = 2e5+1; int k,n,m,ans; struct node { int l,r; bool operator < (const node &rhs) const {return (l+r)<(rhs.l+rhs.r);} }a[maxn],b[maxn]; struct Splay_Tree { int fa[maxn],son[maxn][2],sum[maxn],sz[maxn],val[maxn],rt,tot,cnt[maxn]; void update(int x) { sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x]*cnt[x]; sz[x]=sz[son[x][0]]+sz[son[x][1]]+cnt[x]; } int which(int x) {return son[fa[x]][1]==x;} void rotate(int x) { int f=fa[x],ff=fa[f],w=which(x); if(ff) son[ff][son[ff][1]==f]=x; fa[f]=x,fa[x]=ff,fa[son[x][w^1]]=f,son[f][w]=son[x][w^1],son[x][w^1]=f; update(f),update(x); } void splay(int x) { while(fa[x]) { int y=fa[x],z=fa[y]; if(z) rotate(((son[y][1]==x)^(son[z][1]==y))?x:y); rotate(x); }update(x);rt=x; } int find(int x) { int now=rt; while(val[now]!=x) { if(x<val[now]) {if(son[now][0]) now=son[now][0];else break;} if(x>val[now]) {if(son[now][1]) now=son[now][1];else break;} } return now; } int newnode(int x) {val[++tot]=x,sz[tot]=cnt[tot]=1,sum[tot]=x;return tot;} void insert(int x) { if(!rt) return rt=newnode(x),void(); int now=find(x); if(val[now]==x) return cnt[now]++,sz[now]++,splay(now),void(); son[now][val[now]<x]=newnode(x);fa[tot]=now;splay(now); } int kth(int kk) { int now=rt; while(1) { if(kk<=sz[son[now][0]]) now=son[now][0]; else if(kk<=sz[son[now][0]]+cnt[now]) return now; else kk-=sz[son[now][0]]+cnt[now],now=son[now][1]; } } int solve() { int anss=0,now=kth(sz[rt]/2);splay(now); anss=sz[son[rt][0]]*val[now]-sum[son[rt][0]]+sum[son[rt][1]]-sz[son[rt][1]]*val[now]; return anss; } }s,s2; int res[maxn]; void solve() { for(int i=1;i<=m;i++) { s.insert(b[i].l),s.insert(b[i].r); res[i]=s.solve(); } if(k==1) return write(res[m]+ans); int mn=1e18; for(int i=m;i>=1;i--) { s2.insert(b[i].l),s2.insert(b[i].r); mn=min(mn,s2.solve()+res[i-1]); } write(mn+ans); } signed main() { read(k),read(n); for(int i=1;i<=n;i++) { char s1[3],s3[3]; scanf("%s",s1+1),read(a[i].l),scanf("%s",s3+1),read(a[i].r); if(s1[1]==s3[1]) ans+=abs(a[i].l-a[i].r); else ans++,b[++m]=a[i]; } if(!m) return write(ans),0; sort(b+1,b+m+1); solve(); return 0; }