4071: [Apio2015]巴邻旁之桥
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 99 Solved: 45
[ 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
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
首先如果办公室和家在同一侧,直接将距离加到答案中即可。
那么办公室和家不在同一侧应该如何解决呢?对于k=1,显然只需要将桥建在所有位置的中位数即可。
对于k=2,可以发现每个人都会选择距离家和办公室中点较近的桥行走。那么我们就可以按照家和办公室中点将每个人排序,枚举分割点,将分割点前后的人按k=1的情况分别处理。问题就转化动态维护区间中位数了,可以用平衡树处理。(方法类似bzoj1112)
这道题将所有人按中点排序的思路很好。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define LL long long
#define MAXN 200100
using namespace std;
int n,root,tot,cnt=0;
LL m,x,y,ans=0,mn,a[MAXN],f1[MAXN],f2[MAXN];
char cx,cy;
struct tree_type
{
int l,r;
LL s,rnd,sum,v,w;
}t[MAXN];
struct node
{
LL x,y;
}b[MAXN];
inline LL read()
{
LL ret=0,flag=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') flag=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
return ret*flag;
}
inline char readch()
{
char ch=getchar();
while (ch!='A'&&ch!='B') ch=getchar();
return ch;
}
inline void solve1()
{
F(i,1,n)
{
cx=readch();x=read();cy=readch();y=read();
if (cx==cy) ans+=abs(x-y);
else a[++cnt]=x,a[++cnt]=y,ans++;
}
sort(a+1,a+cnt+1);
F(i,1,cnt) ans+=abs(a[i]-a[cnt>>1]);
printf("%lld\n",ans);
}
inline bool cmp(node n1,node n2){return n1.x+n1.y<n2.x+n2.y;}
inline void pushup(int k){t[k].s=t[t[k].l].s+t[t[k].r].s+t[k].w;t[k].sum=t[t[k].l].sum+t[t[k].r].sum+t[k].w*t[k].v;}
inline void rturn(int &k){LL tmp=t[k].l;t[k].l=t[tmp].r;t[tmp].r=k;t[tmp].s=t[k].s;t[tmp].sum=t[k].sum;pushup(k);k=tmp;}
inline void lturn(int &k){LL tmp=t[k].r;t[k].r=t[tmp].l;t[tmp].l=k;t[tmp].s=t[k].s;t[tmp].sum=t[k].sum;pushup(k);k=tmp;}
inline void ins(int &k,LL x)
{
if (!k){k=++tot;t[k].s=t[k].w=1;t[k].sum=t[k].v=x;t[k].l=t[k].r=0;t[k].rnd=rand();return;}
t[k].s++;t[k].sum+=x;
if (t[k].v==x) t[k].w++;
else if (t[k].v>x){ins(t[k].l,x);if (t[t[k].l].rnd<t[k].rnd) rturn(k);}
else{ins(t[k].r,x);if (t[t[k].r].rnd<t[k].rnd) lturn(k);}
}
inline void del(int &k,LL x)
{
if (t[k].v==x)
{
if (t[k].w>1){t[k].s--;t[k].w--;t[k].sum-=x;}
else if (!t[k].l||!t[k].r) k=t[k].l+t[k].r;
else if (t[t[k].l].rnd<t[t[k].r].rnd){rturn(k);del(k,x);}
else{lturn(k);del(k,x);}
return;
}
t[k].s--;t[k].sum-=x;
if (x<t[k].v) del(t[k].l,x);
else del(t[k].r,x);
}
inline LL getans(int k,int x)
{
LL ln=t[t[k].l].s;
if (ln<x&&ln+t[k].w>=x){m+=t[t[k].l].sum+(x-ln)*t[k].v;return t[k].v;}
else if (ln>=x) return getans(t[k].l,x);
else{m+=t[t[k].l].sum+t[k].w*t[k].v;return getans(t[k].r,x-ln-t[k].w);}
}
inline LL calc(int q)
{
m=0;
LL tmp=getans(root,q);
LL ret=t[root].sum-m*2;
return ret;
}
inline void solve2()
{
F(i,1,n)
{
cx=readch();x=read();cy=readch();y=read();
if (cx==cy) ans+=abs(x-y);
else b[++cnt].x=x,b[cnt].y=y,ans++;
}
sort(b+1,b+cnt+1,cmp);
f1[0]=root=tot=0;
F(i,1,cnt)
{
ins(root,b[i].x);ins(root,b[i].y);
f1[i]=calc(i);
}
f2[cnt+1]=root=tot=0;
D(i,cnt,1)
{
ins(root,b[i].x);ins(root,b[i].y);
f2[i]=calc(cnt-i+1);
}
mn=f1[0]+f2[1];
F(i,1,cnt) mn=min(f1[i]+f2[i+1],mn);
ans+=mn;
printf("%lld\n",ans);
}
int main()
{
LL ff=read();
n=read();
if (ff==1) solve1();
else solve2();
}