UOJ #112. 【APIO2015】Palembang Bridges 线段树

题意

有两排建筑,中间有一条河,给你每个人的起点和终点,现在你需要建k条桥,使得所有人起点到终点的距离最小,k=1或者2,n=100000

分析

先把同在一边的起点和终点给去掉
首先k=1的时候,很容易想到每个人都要过桥,然后每个人的距离就是

|sp|+|tp| | s − p | + | t − p |

p是桥的位置,s和t是起点和终点的位置,那么就是上式的贡献

再很容易发现这其实就是一个中位数模型

k=2的时候,我们考虑一个类似上面的做法
首先一个结论:每个点会选择的桥肯定是离 s+t2 s + t 2 最 近 的
然后我们可以按照 s+t s + t 来排个序,然后枚举断点,断点左边的选左边的桥,断点右边的选右边的桥

然后就转化为一条桥的形式了

断点左边维护一颗权值线段树,断点右边也维护一颗,就可以支持找中位数啦

代码

#include <bits/stdc++.h>
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const ll inf = 1e9;
const ll N = 200010;
const ll lg = 32;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

ll lc[N*lg*2],rc[N*lg*2],c[N*lg*2],s[N*lg*2]; ll rt[2],tot;

void link(ll &u,ll L,ll R,ll k,ll cc)
{
  if(!u) u=++tot;
  if(L==R){c[u] += cc; s[u] += L * cc; return ;}
  ll mid=(L+R)>>1;
  if(k<=mid) link(lc[u],L,mid,k,cc);
  else link(rc[u],mid+1,R,k,cc);
  c[u] = c[lc[u]] + c[rc[u]];
  s[u] = s[lc[u]] + s[rc[u]];
}

ll qry(ll u,ll L,ll R,ll k)
{
  if(L==R) return L;
  ll mid=(L+R)>>1;
  if(c[lc[u]] >= k) return qry(lc[u],L,mid,k);
  else return qry(rc[u],mid+1,R,k-c[lc[u]]);
}

pair<ll,ll> qry2(ll u,ll L,ll R,ll l,ll r) // c s
{
  if(l>r) return MP(0,0);
  if(L == l && R == r) return MP(c[u],s[u]);
  ll mid = (L+R)>>1;
  if(r<=mid) return qry2(lc[u],L,mid,l,r);
  else if(l>mid) return qry2(rc[u],mid+1,R,l,r);
  else
  {
    pii ls = qry2(lc[u],L,mid,l,mid);
    pii rs = qry2(rc[u],mid+1,R,mid+1,r);
    return MP(ls.first + rs.first , ls.second + rs.second);
  }
}

pair<ll,ll> q[N];

ll k,n; ll h[N],x[N],y[N];

int main()
{
  k = read(); n = read(); ll ans = 0,maxx;
  ll nn = 0; for(ll i=1;i<=n;i++)
  {
    char ch1,ch2; ll xx,yy;
    scanf("\n%c%lld\n%c%lld",&ch1,&xx,&ch2,&yy);
    if(ch1 == ch2) ans += abs(xx-yy);
    else ans++ , nn++, x[nn] = xx,y[nn] = yy , h[nn*2-1] = xx,h[nn*2] = yy;
  }n = nn;
  if(k == 1)
  {
    sort(h+1,h+2*n+1); ll mid = h[n];
    for(ll i=1;i<=2*n;i++) ans = ans + abs(mid - h[i]);
    printf("%lld\n",ans);
  }
  else
  {
    for(ll i=1;i<=n;i++) q[i] = MP(x[i] + y[i] , i);
    sort(q+1,q+n+1); rt[0] = rt[1] = tot = 0;
    for(ll i=1;i<=n;i++){link(rt[1],0,inf,x[i],1); link(rt[1],0,inf,y[i],1);}

    ll ls = 0; ll rs = 2*n; ll mid = qry(rt[1],0,inf,rs>>1); ll sum = 0;
    pii lmid = qry2(rt[1],0,inf,0,mid); sum+=mid * lmid.first - lmid.second;
    pii rmid = qry2(rt[1],0,inf,mid+1,inf); sum+=rmid.second - mid * rmid.first;
    maxx = sum; // printf("%lld\n",sum + ans);

    for(ll i=1;i<=n;i++)
    {
      ll pos = q[i].second;
      link(rt[1],0,inf,x[pos],-1); link(rt[1],0,inf,y[pos],-1);
      link(rt[0],0,inf,x[pos],1); link(rt[0],0,inf,y[pos],1);
      ls+=2; rs-=2;

      mid = qry(rt[0],0,inf,ls>>1); sum = 0;
      lmid = qry2(rt[0],0,inf,0,mid); sum+=mid * lmid.first - lmid.second;
      rmid = qry2(rt[0],0,inf,mid+1,inf); sum+=rmid.second - mid * rmid.first;


      mid = qry(rt[1],0,inf,rs>>1);
      lmid = qry2(rt[1],0,inf,0,mid); sum+=mid * lmid.first - lmid.second;
      rmid = qry2(rt[1],0,inf,mid+1,inf); sum+=rmid.second - mid * rmid.first;

      maxx = min(maxx , sum); // printf("%lld\n",sum + ans);
    }
    printf("%lld\n",maxx + ans);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值