1. 程式人生 > >[bzoj4071] [Apio2015]巴鄰旁之橋

[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;
}