APIO2015 八邻旁之桥

传送门

这道题看起来十分的不可做……可能是我数学太差智商太低了orz。

首先如果一个人的家和办公室在同一侧那就完全不用考虑,把结果记下来就行。

然后我们先考虑k=1的情况。因为只能建1座桥,那么我们发现(除去过桥一个单位长度不算)答案就是sigma(abs(ai-x)  + abs(bi-x)),其中ai,bi表示区间的开始和末尾,这样我们发现区间的开始和末尾并没有本质上的区别……所以我们直接把所有端点都排个序然后直接找到中位数(r),答案就是sigma(r-ai) (i<=r) + sigma(ai - r) (i >= r) 然后实际中计算的时候非常的简洁,只要把所有小于中位数的答案加起来,把所有大于中位数的答案也加起来,两者差值+预处理结果就是答案。

那么k=2的时候怎么办呢?这个我又没想到……首先我们可以这么想,找到每个区间的中点,如果中点更靠近左边的桥那么就从左边过桥,否则从右边过桥(这个还是比较显然的)。于是我们先把所有区间按照l+r从小到大排序(中点坐标),再选择枚举一个分割线(分割线左边的全部从左边过桥,右边的从右边过桥),这样我们就把两边分成了两个k=1的情况。

之后的问题就是怎么快速计算。我们使用线段树维护中位数(r),维护区间内元素个数(k)和距离总和(s),这样的话一个区间的答案就是k*loc[r] - s + s1 - k1*loc[r],(前面是中位数左边的,后面是中位数右边的),loc记录这个点所对应的位置。然后分割线另一端的也一样,开两个线段树维护即可。

一开始首先把右边的树填满,之后分割线每移动一次,把对应的从右树中删除,加到左树里。

然后这题有俩坑,第一个是有可能区间的末尾比起点小,这时候要先swap一遍,第二个就是有可能不需要过河…………这样的时候直接特判输出就行…………

看一下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<utility>
#include<map>
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 10000005;
const ll INF = 1000000000000000009;
 
int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int loc[M<<2],n,k,s,ter,tot,cnt,len;
//tot records the number of loc,cnt records the number of segments(node)
char s1[5],s2[5];
ll ans,minn = INF;
struct node
{
    int l,r;
    bool operator < (const node &g)const
    {
        return l + r < g.l + g.r;
    }
}d[M<<2];
struct seg
{
    ll v[M<<3],sz[M<<3];
    void modify(int p,int l,int r,int pos,int x)
    {
    if(l == r)
    {
        v[p] += x * loc[pos];
        sz[p] += x;
        return;
    }
    int mid = (l+r) >> 1;
    if(pos <= mid) modify(p<<1,l,mid,pos,x);
    else modify(p<<1|1,mid+1,r,pos,x);
    v[p] = v[p<<1] + v[p<<1|1];
    sz[p] = sz[p<<1] + sz[p<<1|1];
    }
    int find(int p,int l,int r,int x)
    {
    if(l == r) return l;
    int mid = (l+r) >> 1;
    if(x <= sz[p<<1]) return find(p<<1,l,mid,x);
    else return find(p<<1|1,mid+1,r,x - sz[p<<1]);
    }
    ll size(int p,int l,int r,int kl,int kr)
    {
    if(l == kl && r == kr) return sz[p];
    int mid = (l+r) >> 1;
    if(kr <= mid) return size(p<<1,l,mid,kl,kr);
    else if(kl > mid) return size(p<<1|1,mid+1,r,kl,kr);
    else return size(p<<1,l,mid,kl,mid) + size(p<<1|1,mid+1,r,mid+1,kr);
    }
    ll sum(int p,int l,int r,int kl,int kr)
    {
    if(l == kl && r == kr) return v[p];
    int mid = (l+r) >> 1;
    if(kr <= mid) return sum(p<<1,l,mid,kl,kr);
    else if(kl > mid) return sum(p<<1|1,mid+1,r,kl,kr);
    else return sum(p<<1,l,mid,kl,mid) + sum(p<<1|1,mid+1,r,mid+1,kr);
    }
}t[2];//0 records left ,1 records right

void work()
{
    ll tot1 = 0,tot2 = 0;
    sort(loc+1,loc+1+tot);
    rep(i,1,tot>>1) tot1 += loc[i];
    rep(i,(tot>>1)+1,tot) tot2 += loc[i];
    printf("%lld\n",ans + tot2 - tot1);
}

void solve()
{
    sort(d+1,d+1+cnt);
    sort(loc+1,loc+1+tot);
    len = unique(loc+1,loc+1+tot) - loc - 1;
    rep(i,1,cnt)
    {
    d[i].l = lower_bound(loc+1,loc+1+len,d[i].l) - loc;
    d[i].r = lower_bound(loc+1,loc+1+len,d[i].r) - loc;
    }
    rep(i,1,cnt) t[1].modify(1,1,len,d[i].l,1),t[1].modify(1,1,len,d[i].r,1);
    rep(i,1,cnt)
    {
    t[0].modify(1,1,len,d[i].l,1),t[0].modify(1,1,len,d[i].r,1);
    t[1].modify(1,1,len,d[i].l,-1),t[1].modify(1,1,len,d[i].r,-1);
    int r1 = t[0].find(1,1,len,i),r2 = t[1].find(1,1,len,cnt-i);
    ll d1 = t[0].size(1,1,len,1,r1) * loc[r1] - t[0].sum(1,1,len,1,r1) + t[0].sum(1,1,len,r1,len) - t[0].size(1,1,len,r1,len) * loc[r1];
    ll d2 = t[1].size(1,1,len,1,r2) * loc[r2] - t[1].sum(1,1,len,1,r2) + t[1].sum(1,1,len,r2,len) - t[1].size(1,1,len,r2,len) * loc[r2];
    minn = min(minn,d1+d2);
    }
    printf("%lld\n",minn + ans);
}
int main()
{
    k = read(),n = read();
    rep(i,1,n)
    {
    scanf("%s",s1),s = read();
    scanf("%s",s2),ter = read();
    if(s > ter) swap(s,ter);
    if(s1[0] == s2[0]) ans += ter-s;
    else ans++,loc[++tot] = s,loc[++tot] = ter,d[++cnt].l = s,d[cnt].r = ter;
    }
    if(!cnt)
    {
    printf("%lld\n",ans);
    exit(0);
    }
    if(k == 1) work();
    else solve();
    return 0;
}

 

posted @ 2018-09-20 18:00  CaptainLi  阅读(125)  评论(0编辑  收藏  举报