zoukankan      html  css  js  c++  java
  • 洛谷 P3644 [APIO2015]八邻旁之桥 解题报告

    P3644 [APIO2015]八邻旁之桥

    题目描述

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

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

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

    由于技术上的原因,每一座桥必须刚好连接河的两岸,桥梁必须严格垂直于河流,并且桥与桥之间不能相交。

    当政府建造最多(K)座桥之后,设(D_i)表示第(i)个居民此时开车从家里到办公室的最短距离。请帮助政府建造桥梁,使得(D_1 + D_2 + cdots + D_N)最小。

    输入输出格式

    输入格式:

    输入的第一行包含两个正整数(K)(N),分别表示桥的上限数量和居民的数量。

    接下来(N)行,每一行包含四个参数:(P_i, S_i, Q_i)(T_i),表示第(i)个居民的房子在区域(P_i)(S_i)号建筑上,且他的办公室位于(Q_i)区域的(T_i)号建筑上。

    输出格式:

    输出仅为一行,包含一个整数,表示(D_1 + D_2 + cdots + D_N)的最小值。

    说明

    所有数据都保证:(P_i)(Q_i)为字符 “A” 和 “B” 中的一个, (0 leq S_i, T_i leq 1000000000),同一栋建筑内可能有超过(1)间房子或办公室(或二者的组合,即房子或办公室的数量同时大于等于(1))。

    子任务 1 (8 分) (K = 1)

    (1 leq N leq 1000)

    子任务 2 (14 分) (K = 1)

    (1 leq N leq 100000)

    子任务 3 (9 分) (K = 2)

    (1 leq N leq 100)

    子任务 4 (32 分) (K = 2)

    (1 leq N leq 1000)

    子任务 5 (37 分) (K = 2)

    (1 leq N leq 100000)


    家和办公室在同一列的可以先处理,过河只会过一次可以先处理

    对于剩下的点对((l,r)),若桥放在位置(pos),则距离总和为

    (sum_{i=1}^m |l_i-pos|+|r_i-pos|)(m)为有用的点对的个数

    我们发现家和办公室对答案的贡献计算方法是没有区别的

    则对(k==1)
    问题就转换成了,在区间上选择一个点,使区间上的所有点到这个点的距离和最短。

    是一个贪心问题,我们取这个区间最中间那个点即可。

    (k==2)

    我们思考哪个桥管哪些点

    如果一个桥在点对((l,r))中间,那么走哪个都是无所谓的

    若果不在,则答案为(|pos imes 2-l-r|=|pos imes 2-(l+r)|)((l+r))离哪个(pos)近,就会走哪个桥。

    于是我们可以以((l+r))为关键字进行排序,枚举端点进行统计。

    如何快速统计呢?我们需要支持一个删减元素,查询区间和查询中位数的数据结构

    平衡树和主席树都不错

    事实上主席树常数会优秀很多

    这里写的的fhq_treap


    Code:

    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    #include <algorithm>
    #include <cstring>
    #define ll long long
    #define ls ch[now][0]
    #define rs ch[now][1]
    using namespace std;
    const int N=200010;
    int ch[N][2],val[N],tot;
    ll dat[N],siz[N],sum[N];
    struct node
    {
        ll s,t,k;
        bool friend operator <(node n1,node n2)
        {
            return n1.k<n2.k;
        }
        node(ll s,ll t,ll k)
        {
            this->s=s;
            this->t=t;
            this->k=k;
        }
        node(){}
    }e[N];
    void updata(int now)
    {
        siz[now]=siz[ls]+siz[rs]+1;
        sum[now]=sum[ls]+sum[rs]+dat[now];
    }
    int k0,n,m,root;
    ll ans;
    void split(int now,ll k,int &x,int &y)
    {
        if(!now) {x=y=0;return;}
        if(dat[now]<=k)
            x=now,split(rs,k,rs,y);
        else
            y=now,split(ls,k,x,ls);
        updata(now);
    }
    int Merge(int x,int y)
    {
        if(!x||!y) return x+y;
        if(val[x]<val[y])
        {
            ch[x][1]=Merge(ch[x][1],y);
            updata(x);
            return x;
        }
        else
        {
            ch[y][0]=Merge(x,ch[y][0]);
            updata(y);
            return y;
        }
    }
    int new_node(ll k)
    {
        dat[++tot]=k;val[tot]=rand();siz[tot]=1;sum[tot]=k;
        return tot;
    }
    void Insert(ll k)
    {
        int x,y;
        split(root,k,x,y);
        root=Merge(x,Merge(new_node(k),y));
    }
    void extrack(ll k)
    {
        int x,y,z;
        split(root,k,x,y);
        split(x,k-1,x,z);
        z=Merge(ch[z][0],ch[z][1]);
        root=Merge(x,Merge(z,y));
    }
    ll Rank(int now,int x)//查询排名为x的点
    {
        if(!now) return 0;
        if(x<=siz[ls]) return Rank(ls,x);
        else if(x>siz[ls]+1) return Rank(rs,x-siz[ls]-1);
        else return dat[now];
    }
    ll query(ll k)//返回小于等于k的点的权值之和
    {
        int x,y,Siz=siz[root]>>1;
        ll s=0;
        split(root,k-1,x,y);
        s=sum[x]<<1;
        s+=k*(Siz-siz[x])<<1;
        root=Merge(x,y);
        return s;
    }
    ll cal()
    {
        ll k=Rank(root,siz[root]>>1);
        return sum[root]-query(k);
    }
    ll Ans[N];
    void work2()
    {
        root=tot=0;
        memset(ch,0,sizeof(ch));
        ll sum0=Ans[m];
        for(register int i=m;i;i--)
        {
            Insert(e[i].s);
            Insert(e[i].t);
            sum0=min(sum0,cal()+Ans[i-1]);
        }
        printf("%lld
    ",sum0+ans);
    }
    int main()
    {
        srand(time(0));
        scanf("%d%d",&k0,&n);
        ll s,t;char p,q;
        for(register int i=1;i<=n;i++)
        {
            scanf("
    ");
            scanf("%c%lld %c%lld",&p,&s,&q,&t);
            if(p==q)
                ans+=abs(t-s);
            else
            {
                ans++;
                node tt(s,t,s+t);
                e[++m]=tt;
            }
        }
        sort(e+1,e+1+m);
        for(int i=1;i<=m;i++)
        {
            Insert(e[i].s);
            Insert(e[i].t);
            Ans[i]=cal();
        }
        if(k0==1) printf("%lld
    ",Ans[m]+ans);
        else work2();
        return 0;
    }
    
    

    2018.7.29

  • 相关阅读:
    连接mysql数据库,创建用户模型
    管理信息系统的开发与管理
    加载静态文件,父模板的继承和扩展
    从首页问答标题到问答详情页
    首页列表显示全部问答,完成问答详情页布局
    制作首页的显示列表
    发布功能完成
    登录之后更新导航
    完成登录功能,用session记住用户名
    完成注册功能
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9385979.html
Copyright © 2011-2022 走看看