zoukankan      html  css  js  c++  java
  • APIO2015 八邻旁之桥

    传送门

    题目大意

    两条水平的直线,距离为$1$,给定$n$对坐标,坐标是在某一条直线上的某一个位置,你可以建立$k$竖直的直线$(kleq 2)$,要使得每对坐标只沿着直线移动从第一个坐标到第二个坐标的距离和最小。

    题解

    首先可以求出在一对坐标在同一条水平直线上的代价,对于需要跨越的,都经过了竖直直线的$1$的距离,也都预先算出来。

    考虑$k=1$的情况,设每对坐标的水平位置是$(x_1,x_2)$,直线放在了$pos$的位置,那么代价是$sum(|x_1-pos|+|x_2-pos|)$。

    发现$x_1$,$x_2$对答案的贡献是独立的,且很显然当$pos$取所有$x_1,x_2$的中位数时,距离最小。

    如何扩展到$k=2$?

    设条竖直线的坐标是$pos_1,pos_2$(不妨设$pos_1<pos_2$),若$x_1,x_2$会选择$pos_1$,那么$|x_1-pos_1|+|x_2-pos_1|leq |x_1-pos_2|+|x_2-pos2|$,冷静分析一下不难得出只需比较$frac{x_1+x_2}{2}$与$pos_1,pos_2$的远近即可。

    考虑对所有对坐标按照$x_1+x_2$,排序,这样一定满足选择$pos_1$的一定是一个前缀,选择$pos_2$的一定是一个后缀,那么问题就转化为两个$k=1$的子问题了。

    直接枚举前缀后缀的划分位置,用权值线段树维护左右部分中位数大小,用前缀和优化算出所有数到中位数的距离即可。

    复杂度$O(nlog n)$。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 100020
    using namespace std;
    namespace IO{
    	int Top=0; char SS[20];
    	void write(LL x){
    		if(!x){putchar('0');return;} if(x<0) x=-x,putchar('-');
    		while(x) SS[++Top]=x%10,x/=10;
    		while(Top) putchar(SS[Top]+'0'),--Top;
    	}
    	int read(){
    		int nm=0,fh=1; char cw=getchar();
    		for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    		for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    		return nm*fh;
    	}
    }
    using namespace IO; char s1[20],s2[20];
    int rt[2],n,m,A[M<<2],B[M<<2],K,cnt[2],od[M];
    int G[M<<2],L[M<<2][2],R[M<<2][2],ct[M<<2][2];
    LL res,sum[M<<2][2];
    bool cmp(int x,int y){return A[x]+B[x]<A[y]+B[y];}
    LL solve1(){
    	sort(G+1,G+m+1); LL nw=0;
    	for(int i=1;i<=m;i++) nw+=abs(G[m>>1]-G[i]);
    	return nw;
    }
    void ins(int &x,int l,int r,int pos,int dt,int kd){
    	if(!x) x=++cnt[kd]; ct[x][kd]+=dt,sum[x][kd]+=G[pos]*dt;
    	if(l==r) return; int mid=((l+r)>>1);
    	if(pos<=mid) ins(L[x][kd],l,mid,pos,dt,kd);
    	else ins(R[x][kd],mid+1,r,pos,dt,kd);
    }
    int gtnum(int x,int l,int r,int rk,int kd){
    	if(l==r) return l; int mid=((l+r)>>1);
    	if(rk<=ct[L[x][kd]][kd]) return gtnum(L[x][kd],l,mid,rk,kd);
    	return gtnum(R[x][kd],mid+1,r,rk-ct[L[x][kd]][kd],kd);
    }
    LL qry(int x,int l,int r,int pos,int kd){
    	if(!ct[x][kd]) return 0ll;
    	if(r<=pos) return (LL)ct[x][kd]*(LL)G[pos]-sum[x][kd];
    	if(pos<=l) return sum[x][kd]-(LL)G[pos]*(LL)ct[x][kd];
    	int mid=((l+r)>>1);
    	return qry(L[x][kd],l,mid,pos,kd)+qry(R[x][kd],mid+1,r,pos,kd);
    }
    LL calc(LL kd){
    	if(!ct[rt[kd]][kd]) return 0ll; int rk=(ct[rt[kd]][kd]+1); rk>>=1;
    	int md=gtnum(rt[kd],1,m,rk,kd);
    	return qry(rt[kd],1,m,md,kd);
    }
    LL solve2(){
    	sort(od+1,od+n+1,cmp);
    	sort(G+1,G+m+1),m=unique(G+1,G+m+1)-(G+1);
    	for(int i=1;i<=n;i++){
    		int t1=lower_bound(G+1,G+m+1,A[od[i]])-G;
    		int t2=lower_bound(G+1,G+m+1,B[od[i]])-G;
    		ins(rt[0],1,m,t1,1,0),ins(rt[0],1,m,t2,1,0);
    	} LL ans=calc(0);
    	for(int i=n;i;i--){
    		int t1=lower_bound(G+1,G+m+1,A[od[i]])-G;
    		int t2=lower_bound(G+1,G+m+1,B[od[i]])-G;
    		ins(rt[0],1,m,t1,-1,0),ins(rt[0],1,m,t2,-1,0);
    		ins(rt[1],1,m,t1,1,1),ins(rt[1],1,m,t2,1,1);
    		ans=min(ans,calc(0)+calc(1));
    	} return ans;
    }
    int main(){
    	K=read(),n=read();
    	for(int i=1;i<=n;i++){
    		scanf("%s",s1),A[i]=read(),scanf("%s",s2),B[i]=read();
    		if(A[i]>B[i]) swap(A[i],B[i]);
    		if(s1[0]==s2[0]) res+=B[i]-A[i],i--,n--;
    		else G[++m]=A[i],G[++m]=B[i],od[i]=i,res++;
    	} write(((K&1)?solve1():solve2())+res);
    	putchar('
    ');return 0;
    }
  • 相关阅读:
    [YNOI2017][bzoj4811][luogu3613] 由乃的OJ/睡觉困难综合症 [压位+树链剖分+线段树]
    [bzoj3270] 博物馆 [期望+高斯消元]
    [bzoj4372] 烁烁的游戏 [动态点分治+线段树+容斥原理]
    [Codeforces438E][bzoj3625] 小朋友和二叉树 [多项式求逆+多项式开根]
    [bzoj3813] 奇数国 [线段树+欧拉函数]
    [BZOJ4205][FJ2015集训] 卡牌配对 [建图+最大流]
    Git常见问题解决办法
    电脑常用快捷键
    egret.Shape渲染集合图形
    TypeScript语法学习--变量的声明
  • 原文地址:https://www.cnblogs.com/OYJason/p/9830299.html
Copyright © 2011-2022 走看看