4071:[apio2015]巴邻旁之桥
Time limit: 2000 ms
Memory limit: 262144 KB
Description
The city of Palembang is separated by Musi River into two zones. Let’s call them zone A and zone B.
Each zone consists of exactly 1,000,000,001 buildings along the respective side of the river, conveniently numbered 0 through 1,000,000,000. The distance between every pair of adjacent buildings is 1 unit of distance. The width of the river is 1 unit of distance as well. Building i in zone A is located on exactly the opposite side of building i in zone B.
N citizens live and work in the city. Citizen i’s house is in zone Pi, building Si, while his office is in zone Qi, building Ti. If a citizen must cross the river to go from his house to his office, he must take a boat. This has been uncomfortable, so the government has decided to build at most K bridges over the river, so that the citizens can go to work by driving. Each bridge must be built exactly between two opposite buildings in the two zones. The bridges must be strictly perpendicular to the river. The bridges must not overlap each other.
Let Di be the minimum distance citizen i has to drive to go from his house to his office, after the government has built at most K bridges. Help the government build the bridges in such a way that the sum D1 + D2 + … + DN is minimized.
Input Format
The first line contains two integers K and N. Each of the next N lines contains four tokens Pi, Si, Qi, and Ti.
Output Format
A single line containing the minimum sum of the distances.
Sample Input 1
1 5
B 0 A 4
B 1 B 3
A 5 B 7
B 2 A 6
B 1 A 7
Sample Output 1
24
Sample Input 2
2 5
B 0 A 4
B 1 B 3
A 5 B 7
B 2 A 6
B 1 A 7
Sample Output 2
22
Explanation
This is the illustration for both sample inputs.
Here is one possible solution for sample input 1. The pink stripe segment denotes a bridge.
And this is a possible solution for sample input 2:
Subtasks
For each subtask,
Pi and Qi will be either a character ‘A’ or a character ‘B’.
0 ≤ Si, Ti ≤ 1,000,000,000
More than one house or office (or combination of both) can be located in the same building.
Subtask 1 (8 points)
K = 1
1 ≤ N ≤ 1,000
Subtask 2 (14 points)
K = 1
1 ≤ N ≤ 100,000
Subtask 3 (9 points)
K = 2
1 ≤ N ≤ 100
Subtask 4 (32 points)
K = 2
1 ≤ N ≤ 1,000
Subtask 5 (37 points)
K = 2
1 ≤ N ≤ 100,000
权值线段树动态维护中位数。
(家和单位在同一侧的提前算好)
当把所求的计算式列出之后,可以发现最优解是所有办公室和家的位置的中位数。
对于k=1的,中位数可以直接求出。
对于k=2的,可以发现:按照每个人的家和办公室的中点排序后,一定存在一个分割点使得前缀都走左边的桥,后缀都走右边的桥(因为走靠近中点的桥不会更差)。
于是我们枚举分割点,离散化后用权值线段树动态维护两个区间的中位数求解即可。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <vector>
#define pb push_back
#define M 300005
#define LL long long
using namespace std;
LL ans[M];
int size,n,k,cnt;
int ls[M],d[M];
char s1[10],s2[10];
struct data
{
int x[3];
}a[M];
struct Segtree
{
int size;
LL sum;
}t[M<<2];
int z[10];
void lisan()
{
sort(ls+1,ls+1+cnt);
size=unique(ls+1,ls+1+cnt)-ls-1;
}
int Hash(int x)
{
return lower_bound(ls+1,ls+1+size,x)-ls;
}
bool cmp(data a,data b)
{
return a.x[1]+a.x[2]<b.x[1]+b.x[2];
}
void Update(int x)
{
t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
t[x].size=t[x<<1].size+t[x<<1|1].size;
}
void Build(int x,int l,int r)
{
if (l==r)
{
t[x].sum=t[x].size=0;
return;
}
int m=(l+r)>>1;
Build(x<<1,l,m);
Build(x<<1|1,m+1,r);
Update(x);
}
void Insert(int x,int l,int r,int k)
{
if (l==r)
{
t[x].sum+=d[l];
t[x].size++;
return;
}
int m=(l+r)>>1;
if (k<=m) Insert(x<<1,l,m,k);
else Insert(x<<1|1,m+1,r,k);
Update(x);
}
LL Getsum(int x,int l,int r,int cnt)
{
if (t[x].size<=cnt)
return t[x].sum;
if (l==r)
return 1LL*cnt*d[l];
int m=(l+r)>>1;
if (t[x<<1].size>=cnt) return Getsum(x<<1,l,m,cnt);
else return t[x<<1].sum+Getsum(x<<1|1,m+1,r,cnt-t[x<<1].size);
}
LL Query(LL k)
{
LL s=Getsum(1,1,size,k);
return t[1].sum-2LL*s;
}
int main()
{
scanf("%d%d",&k,&n);
LL pre=0;
int tot=0;
cnt=0;
for (int i=1;i<=n;i++)
{
int x1,x2;
scanf("%s%d%s%d",s1,&x1,s2,&x2);
if (s1[0]==s2[0])
{
pre+=abs(x1-x2);
continue;
}
pre++;
a[++tot].x[1]=x1,a[tot].x[2]=x2;
ls[++cnt]=x1,ls[++cnt]=x2;
}
if (cnt)
{
lisan();
n=tot;
for (int i=1;i<=n;i++)
d[Hash(a[i].x[1])]=a[i].x[1],d[Hash(a[i].x[2])]=a[i].x[2];
Build(1,1,size);
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
Insert(1,1,size,Hash(a[i].x[1]));
Insert(1,1,size,Hash(a[i].x[2]));
ans[i]=Query(i);
}
}
if (k==1)
cout<<ans[n]+pre<<endl;
else
{
LL Ans=ans[n];
if (size)
{
Build(1,1,size);
for (int i=n;i>1;i--)
{
Insert(1,1,size,Hash(a[i].x[1]));
Insert(1,1,size,Hash(a[i].x[2]));
Ans=min(Ans,ans[i-1]+Query(n-i+1));
}
}
cout<<Ans+pre<<endl;
}
return 0;
}
这道题的关键在于发现要求的是中位数,以及按照中点排序后的性质。