4071: [Apio2015]巴邻旁之桥

4071: [Apio2015]巴邻旁之桥

Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 296 Solved: 130
[Submit][Status][Discuss]
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

子任务

所有数据都保证:Pi 和 Qi 为字符 “A” 和 “B” 中的一个, 0≤Si,Ti≤1000000000,同一栋建筑内可能有超过 1 间房子或办公室(或二者的组合,即房子或办公室的数量同时大于等于 1)。

子任务 1 (8 分)

K=1

1≤N≤1000

子任务 2 (14 分)

K=1

1≤N≤100000

子任务 3 (9 分)

K=2

1≤N≤100

子任务 4 (32 分)

K=2

1≤N≤1000

子任务 5 (37 分)

K=2

1≤N≤100000
Source

[Submit][Status][Discuss]

先把起点和终点相同的情形特判掉,直接计入答案
假设 K=1 ,那么把剩下的端点排序,建在中位数处显然是最优的
那么 K=2 的情形该怎么处理。。。。?
好吧彻底不会了。。。死都想不出来系列
对于每个人,拿出他家和上班位置的中点
每段路按照中点坐标升序排好
不加证明地给出一个结论。。。
一定存在一个分割点,使得左边都是走左边的桥,右边走右边的桥
非常显然。。。因为走更靠近自己路程中点的那座桥一定不会更劣的
那么就变成维护动态中位数了。。。随便用个set之类的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>
#define fr first
#define sc second
#define mp(a,b) (make_pair((a),(b)))
#define min(a,b) ((a) < (b) ? (a) : (b))
using namespace std;

const int maxn = 2E5 + 20;
typedef long long LL;

int n,m,K,tot,siz,cur = 1,A[maxn],S[maxn],T[maxn],c[maxn];
LL Ans,Min = 1E16,s[maxn],B1[maxn],B2[maxn];

set <pair<int,int> > st;
pair <int,int> pr[maxn];
set <pair<int,int> > :: iterator it;

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

inline int getpos()
{
    char ch = getchar();
    while (ch != 'A' && ch != 'B') ch = getchar();
    return ch == 'A' ? 0 : 1;
}

inline void Modify(int pos)
{
    for (int i = pos; i <= cur; i += i&-i) s[i] += 1LL * A[pos],++c[i];
}

inline void Insert(int pos,bool flag)
{
    if (flag)
    {
        st.insert(mp(S[pos],++siz)); st.insert(mp(T[pos],++siz));
        Modify(S[pos]); Modify(T[pos]); it = st.begin();
    }
    else
    {
        pair <int,int> now = (*it); int typ;
        pair <int,int> X = mp(S[pos],++siz);
        pair <int,int> Y = mp(T[pos],++siz);
        st.insert(X); st.insert(Y);
        Modify(S[pos]); Modify(T[pos]);
        if (X >= now && Y >= now) typ = 1;
        else if (X <= now && Y <= now) typ = 2; else typ = 3;
        if (typ == 1) ++it; else if (typ == 2) --it;
    }
}

inline LL Check()
{
    int p = (*it).fr,cnt = 0; LL now = 0,sum = 0;
    for (int i = cur; i > 0; i -= i&-i) sum += s[i],cnt += c[i];
    for (int i = p; i > 0; i -= i&-i) sum -= s[i],cnt -= c[i];
    now += sum - 1LL * cnt * A[p]; cnt = sum = 0;
    for (int i = p - 1; i > 0; i -= i&-i) sum += s[i],cnt += c[i];
    now += 1LL * cnt * A[p] - sum; return now;
}

inline void Work(LL *B)
{
    Insert(pr[1].sc,1); B[1] = Check();
    for (int i = 2; i < tot; i++)
        Insert(pr[i].sc,0),B[i] = Check();
}

void Solve2()
{
    sort(pr + 1,pr + tot + 1);
    sort(A + 1,A + tot * 2 + 1);
    for (int i = 2; i <= tot * 2; i++)
        if (A[i] != A[i - 1]) A[++cur] = A[i];
    for (int i = 1; i <= tot; i++)
    {
        S[i] = lower_bound(A + 1,A + cur + 1,S[i]) - A;
        T[i] = lower_bound(A + 1,A + cur + 1,T[i]) - A;
    }
    Work(B1); memset(s,0,sizeof(s)); memset(c,0,sizeof(c));
    reverse(pr + 1,pr + tot + 1); st.clear();
    Work(B2); reverse(B2 + 1,B2 + tot + 1);
    for (int i = 1; i < tot; i++) Min = min(Min,B1[i] + B2[i + 1]);
}

void Solve1()
{
    Min = 0; sort(A + 1,A + tot * 2 + 1);
    for (int i = 1; i <= tot; i++)
        Min += 1LL * (abs(A[tot] - S[i]) + abs(A[tot] - T[i]));
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    K = getint(); n = getint();
    for (int i = 1; i <= n; i++)
    {
        int p1,q1,p2,q2;
        p1 = getpos(); q1 = getint();
        p2 = getpos(); q2 = getint();
        if (q1 > q2) swap(q1,q2);
        if (p1 == p2) Ans += 1LL * (q2 - q1);
        else
        {
            S[++tot] = q1; T[tot] = q2;
            pr[tot] = mp(q1 + q2 >> 1,tot);
            A[tot * 2] = q1; A[tot * 2 - 1] = q2;
        }
    }
    if (!tot) {cout << Ans << endl; return 0;}
    if (K == 1) Solve1(); else Solve2();
    cout << Ans + Min + 1LL * tot << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值