BZOJ4071 & 洛谷3644:[APIO2015]巴邻旁之桥——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4071

https://www.luogu.org/problemnew/show/P3644

一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B。

每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000。相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度。区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对。

城市中有 N 个居民。第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上。一个居民的房子和办公室可能分布在河的两岸,这样他就必须要搭乘船只才能从家中去往办公室,这种情况让很多人都觉得不方便。为了使居民们可以开车去工作,政府决定建造不超过 K 座横跨河流的大桥。

由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。当政府建造最多 K 座桥之后,设 Di 表示第 i 个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得 D1+D2+?+DN 最小。

参考:https://www.cnblogs.com/zhenghaotian/p/8304917.html

对于同岸和走桥的代价先预处理,下面不在阐述。

k=1时,相当于找到一个点,使得所有起点和终点到该点的距离和最小。则我们排序在中间两点中间建桥则一定最优。就不证明了。

k=2时不能按k=1做(因为起点和终点需要用的桥是一样的),但是选桥的代价为(A起点,B桥,C终点)A->B->C,显然选一个近的桥最优,用程序表达的话就是离AC中点最近的桥。

所以以此排序,枚举分界点,其左右都是k=1的情况,用线段树做即可。

(简单聊下心路历程:开始k=1秒后想k=2,没考虑起点终点桥一样以为三分可过,结果第二个样例就跪了,后来思考之后排序后三分是O(nlog^2n)结果洛谷评测机死活没卡过去TAT果然还是太菜了我)

#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200100;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline char getc(){
    char ch=0;
    while(ch!=‘A‘&&ch!=‘B‘)ch=getchar();
    return ch;
}
struct node{
    int l,r;
}q[N];
int m,tot,b[N];
ll num[2][N*4],sum[2][N*4],L[2],R[2];
bool cmp(node a,node b){return a.l+a.r<b.l+b.r;}
void LSH(){
    sort(b+1,b+m+1);
    m=unique(b+1,b+m+1)-b-1;
    for(int i=1;i<=tot;i++){
    q[i].l=lower_bound(b+1,b+m+1,q[i].l)-b;
    q[i].r=lower_bound(b+1,b+m+1,q[i].r)-b;
    }
}
void insert(int a,int l,int r,int k,ll w,int p){
    num[p][a]+=w,sum[p][a]+=w*b[k];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)insert(a*2,l,mid,k,w,p);
    else insert(a*2+1,mid+1,r,k,w,p);
}
int find(int a,int l,int r,int k,int p){
    if(l==r){
    L[0]+=sum[p][a],L[1]+=num[p][a];
    return b[l];
    }
    int mid=(l+r)>>1;
    if(num[p][a*2]>=k){
    R[0]+=sum[p][a*2+1],R[1]+=num[p][a*2+1];
    return find(a*2,l,mid,k,p);
    }else{
    L[0]+=sum[p][a*2],L[1]+=num[p][a*2];
    return find(a*2+1,mid+1,r,k-num[p][a*2],p);
    }
}
int main(){
    int k=read(),n=read();
    ll ans=0;
    for(int i=1;i<=n;i++){
        char ch1=getc();
        ll u=read();
        char ch2=getc();
        ll v=read();
        if(ch1==ch2){
        ans+=abs(u-v);
        continue;
        }
        ans++;
        b[++m]=u,b[++m]=v;
        if(k==2){
        q[++tot]=(node){u,v};
        }
    }
    if(k==1){
        sort(b+1,b+m+1);
        for(int i=1,j=m;i<j;i++,j--)ans+=b[j]-b[i];
        printf("%lld\n",ans);
    }else{
    if(!tot){
        printf("%lld\n",ans);
        return 0;
    }
        sort(q+1,q+tot+1,cmp);
    LSH();
    for(int i=1;i<=tot;i++){
        insert(1,1,m,q[i].l,1,1);
        insert(1,1,m,q[i].r,1,1);
    }
    int x=find(1,1,m,tot,1);
    ll tmp=x*L[1]-L[0]+R[0]-x*R[1];
    for(int i=1;i<tot;i++){
        insert(1,1,m,q[i].l,1,0);
        insert(1,1,m,q[i].r,1,0);
        insert(1,1,m,q[i].l,-1,1);
        insert(1,1,m,q[i].r,-1,1);
        ll all=0;
        L[0]=L[1]=R[0]=R[1]=0;
        x=find(1,1,m,i,0);
        all+=x*L[1]-L[0]+R[0]-x*R[1];
        L[0]=L[1]=R[0]=R[1]=0;
        x=find(1,1,m,tot-i,1);
        all+=x*L[1]-L[0]+R[0]-x*R[1];
        tmp=min(tmp,all);
    }
    printf("%lld\n",ans+tmp);
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/luyouqi233/p/8669351.html

时间: 2024-10-12 20:45:49

BZOJ4071 & 洛谷3644:[APIO2015]巴邻旁之桥——题解的相关文章

【BZOJ 4071】[apio2015]巴邻旁之桥

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 respectiv

【bzoj4071】[Apio2015]巴邻旁之桥 Treap

题目描述 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000.相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度.区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对. 城市中有 N 个居民.第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上.一个居民的房子和办公室可能分布在河

4071: [Apio2015]巴邻旁之桥

Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000.相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度.区域 A 中的 i 号建筑物恰好与区域 B 中的 i 号建筑物隔河相对. 城市中有 N 个居民.第 i 个居民的房子在区域 Pi 的 Si 号建筑上,同时他的办公室坐落在 Qi 区域的 Ti 号建筑上.一个居民的房子和办公

[APIO2015]八邻旁之桥

题目描述 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 AA 和区域 BB . 每一块区域沿着河岸都建了恰好 10000000011000000001 栋的建筑,每条岸边的建筑都从 00 编号到 10000000001000000000 .相邻的每对建筑相隔 11 个单位距离,河的宽度也是 11 个单位长度.区域 AA 中的 ii 号建筑物恰好与区域 BB 中的 ii 号建筑物隔河相对. 城市中有 NN 个居民.第 ii 个居民的房子在区域 P_iPi? 的 S_iSi? 号建筑上,同时

APIO2015 八邻旁之桥/巴邻旁之桥

题目描述: bz luogu 题解: 贪心+权值线段树. $K=1$的时候,答案为$\sum |x-l| + |x-r|$,所以所有端点排序后取中位数即可. $K=2$的时候,一定是左边的一些走左边的桥,右边的一些走右边的桥. 问题是按什么顺序排序. 答案是按线段中点排序. 原因是,对于河两岸的一对点和两座桥,选择的一定是离线段中点近的那个. 考虑如何快速计算答案,我们可以用权值线段树维护区间和与中位数.(当然也可以用平衡树) 代码: #include<cstdio> #include<

洛谷 P2878 [USACO07JAN]保护花朵Protecting the Flowers 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=2878 (本来想放bzoj的链接  然而bzoj这道题挂了) [题目描述] 约翰留下他的N(N<=100000)只奶牛上山采木.他离开的时候,她们像往常一样悠闲地在草场里吃草.可是,当他回来的时候,他看到了一幕惨剧:牛们正躲在他的花园里,啃食着他心爱的美丽花朵!为了使接下来花朵的损失最小,约翰赶紧采取行动,把牛们送回牛棚. 牛们从1到N编

洛谷 P3376 【模板】网络最大流 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problemnew/show/3376 题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行包含三个正整数ui.vi.wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi) 输出格式: 一行,包含一个正

洛谷P1789【Mc生存】插火把 题解

题目传送门 这道题目可以纯暴力: #include<bits/stdc++.h> //Minecraft 666 using namespace std; int a[110][110]; int n,m,k,ans; int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); a[x-2

洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth 题解

题目传送门 这道题暴力就能解决. #include<bits/stdc++.h> using namespace std; int xi; int day=0,ans[8]; int main() { int n; cin>>n; for(int j=0;j<n;j++) { for(int i=1;i<=31;i++) { day++; if(i==13) { xi=day%7; if(xi==0)ans[7]++; else ans[xi]++; } } if((1