[APIO2015]八邻旁之桥

嘟嘟嘟


这题拿到后就瞬秒\(k = 1\)的情况,跟这题一模一样。


所以我最后也只拿了31分……


正解要推出这么个性质,就是如果只有一座桥,那么所有城市和办公室的中位数就是这座桥的位置(然而我的\(k = 1\)做法显然没有用到这一点……)。
怎么证明呢?
先用式子表示一下:就是我们要找到一个\(x\),满足\(\sum_{i = 1} ^ {m} |L_i - x| + |R_i - x|\)最小,即\(\sum _ {i = 1} ^ {2m} |a_i - x|\)最小。
假设\(x\)现在是中位数,那么如果我们把\(x\)变小或变大了一位(这里的“一位”指的是和某一个数的大小关系发生了改变),带来的代价就是\(m * \Delta x - (m - 1) * \Delta x = \Delta x\),这个值显然大于0,所以移动必定会带来正的代价。那么中位数就是最优的。


那么\(k = 2\)的情况怎么办呢?
观察发现,每一个人的距离和\(|L_i - R_i|\)以及距离桥的距离有关。因此我们取\(L_i, R_i\)的中点,看中点离哪边的桥近就走哪边。
所以我们先把所有人按各自的中点排序(即\(L_i + R_i\)),然后枚举两座桥的分界点,即分界点左侧的人都上一座桥,右侧的人上另一座桥。然后两边就变成了\(k = 1\)的情况,分别求中位数即可。
因为要支持动态删点和加点,所以开两棵权值线段树,支持查找第\(k\)大和区间求和操作。
找完中位数后计算答案的方法和上面说的那道题一样,用前缀和整体相减即可。


然后我因为按离散化后的值的中位数排序gg了好半天。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const ll INF = 1e18;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int K, n;

char s1[2], s2[2];
int a[maxn], b[maxn], t[maxn << 1], cnt[2];
ll suma[maxn], sumb[maxn];
In void work0()
{
  ll ans = INF, SUM = 0;
  for(int i = 1; i <= n; ++i)
    {
      scanf("%s", s1); int val1 = read();
      scanf("%s", s2); int val2 = read();
      if(s1[0] == s2[0]) SUM += abs(val1 - val2);
      else
	    {
	      a[++cnt[0]] = val1, b[cnt[0]] = val2;
	      if(s1[0] == 'B') swap(a[cnt[0]], b[cnt[0]]);
	    }
    }
  for(int i = 1; i <= cnt[0]; ++i) t[i] = a[i];
  for(int i = 1; i <= cnt[0]; ++i) t[i + cnt[0]] = b[i];
  sort(t + 1, t + (cnt[0] << 1) + 1);
  cnt[1] = unique(t + 1, t + (cnt[0] << 1) + 1) - t - 1;
  for(int i = 1; i <= cnt[0]; ++i) a[i] = lower_bound(t + 1, t + cnt[1] + 1, a[i]) - t;
  for(int i = 1; i <= cnt[0]; ++i) b[i] = lower_bound(t + 1, t + cnt[1] + 1, b[i]) - t;
  sort(a + 1, a + cnt[0] + 1), sort(b + 1, b + cnt[0] + 1);
  for(int i = 1; i <= cnt[0]; ++i) suma[i] = suma[i - 1] + t[a[i]];
  for(int i = 1; i <= cnt[0]; ++i) sumb[i] = sumb[i - 1] + t[b[i]];
  suma[cnt[0] + 1] = suma[cnt[0]];
  sumb[cnt[0] + 1] = sumb[cnt[0]];
  for(int i = 1, pos; i <= cnt[1]; ++i)
    {
      ll sum = 0;
      pos = lower_bound(a + 1, a + cnt[0] + 1, i) - a;
      if(pos > 0) sum = 1LL * t[i] * (pos - 1) - suma[pos - 1] + suma[cnt[0]] - suma[pos - 1] - 1LL * t[i] * (cnt[0] - pos + 1);
      pos = lower_bound(b + 1, b + cnt[0] + 1, i) - b;
      if(pos > 0) sum += 1LL * t[i] * (pos - 1) - sumb[pos - 1] + sumb[cnt[0]] - sumb[pos - 1] - 1LL * t[i] * (cnt[0] - pos + 1);
      ans = min(ans, sum);
    }
  if(ans == INF) ans = 0;
  write(ans + SUM + cnt[0]), enter;
}

struct Node
{
  int L, R;
  In bool operator < (const Node& oth)const
  {
    return L + R < oth.L + oth.R;
  }
}q[maxn];
int qcnt = 0, tot = 0;

struct Tree
{
  int l[maxn << 3], r[maxn << 3], siz[maxn << 3];
  ll sum[maxn << 3];
  In void build(int L, int R, int now)
  {
    l[now] = L, r[now] = R;
    sum[now] = siz[now] = 0; 
    if(L == R) return;
    int mid = (L + R) >> 1;
    build(L, mid, now << 1);
    build(mid + 1, R, now << 1 | 1);
  }
  In void pushup(int now)
  {
    sum[now] = sum[now << 1] + sum[now << 1 | 1];
    siz[now] = siz[now << 1] + siz[now << 1 | 1];
  }
  In void update(int id, int now, int flg)
  {
    if(l[now] == r[now])
      {
	    sum[now] += 1LL * t[id] * flg;
	    siz[now] += flg; return;
      }
    int mid = (l[now] + r[now]) >> 1;
    if(id <= mid) update(id, now << 1, flg);
    else update(id, now << 1 | 1, flg);
    pushup(now);
  }
  In ll kth(int k, int now)
  {
    if(l[now] == r[now]) return t[l[now]] * k;
    if(siz[now << 1] >= k) return kth(k, now << 1);
    else return siz[now << 1] + kth(k - siz[now << 1], now << 1 | 1);
  }
  In ll query_low(int k, int now, db d)
  {
    if(l[now] == r[now]) return d * k - 1LL * t[l[now]] * k;
    if(siz[now << 1] > k) return query_low(k, now << 1, d);
    else if(siz[now << 1] == k) return d * siz[now << 1] - sum[now << 1];
    else return d * siz[now << 1] - sum[now << 1] + query_low(k - siz[now << 1], now << 1 | 1, d);
  }
  In ll query_upp(int k, int now, db d)
  {
    if(l[now] == r[now]) return 1LL * t[l[now]] * k - d * k;
    if(siz[now << 1 | 1] > k) return query_upp(k, now << 1 | 1, d);
    else if(siz[now << 1 | 1] == k) return sum[now << 1 | 1] - d * siz[now << 1 | 1];
    else return sum[now << 1 | 1] - d * siz[now << 1 | 1] + query_upp(k - siz[now << 1 | 1], now << 1, d);
  }
  In ll Query(int len)
  {
    if(!len) return 0;
    ll mid = kth(len >> 1, 1);
    return query_low(len >> 1, 1, mid) + query_upp(len >> 1, 1, mid);
  }
}t1, t2;

In void work1()
{
  ll ans = INF, SUM = 0;
  for(int i = 1; i <= n; ++i)
    {
      scanf("%s", s1); int val1 = read();
      scanf("%s", s2); int val2 = read();
      if(s1[0] == s2[0]) SUM += abs(val1 - val2);
      else q[++qcnt] = (Node){val1, val2}, t[++tot] = val1, t[++tot] = val2;
    }
  if(!qcnt) {write(SUM), enter; return;}
  sort(t + 1, t + tot + 1);
  tot = unique(t + 1, t + tot + 1) - t - 1;
  t1.build(1, tot, 1), t2.build(1, tot, 1);
  sort(q + 1, q + qcnt + 1);
  for(int i = 1; i <= qcnt; ++i)
    {
      q[i].L = lower_bound(t + 1, t + tot + 1, q[i].L) - t;
      q[i].R = lower_bound(t + 1, t + tot + 1, q[i].R) - t;
      t2.update(q[i].L, 1, 1), t2.update(q[i].R, 1, 1);
    }
  for(int i = 1; i <= qcnt + 1; ++i)
    {
      ll sum = t1.Query((i - 1) << 1) + t2.Query((qcnt - i + 1) << 1);
      ans = min(ans, sum);
      if(i > qcnt) break;
	  t1.update(q[i].L, 1, 1), t1.update(q[i].R, 1, 1);
	  t2.update(q[i].L, 1, -1), t2.update(q[i].R, 1, -1);
	}
  write(ans + SUM + qcnt), enter;
}

int main()
{
  K = read(), n = read();
  if(K == 1) {work0(); return 0;}
  if(K == 2) {work1(); return 0;}
  return 0;
}
posted @ 2019-03-20 10:28  mrclr  阅读(219)  评论(0编辑  收藏  举报