[JZOJ6075]【GDOI2019模拟2019.3.20】桥【DP】【线段树】-程序员宅基地

Description

在这里插入图片描述
N,M<=100000,S,T<=1e9

Solution

首先可以感受一下,我们把街道看成一行,那么只有给出的2n个点的纵坐标是有用的,于是我们可以将坐标离散化至O(n)级别。

显然出发地和目的地的地位是相同的,因此我们强制要求从编号小的街道走向标号大的街道。

我们考虑一个朴素的DP,记\(F[i][j]\)表示当前转移到了第i行,连接第i-1行和第i行的桥梁位于位置j
枚举上一行的桥梁在哪里,我们可以得到一个大概的转移式子\(F[i][j]=S[i][j]+min(F[i-1][k]+c\left|j-k\right|)\),其中\(S[i][j]\)为只与i,j有关的一个常数,可以理解成当前行的目的地到达情况和上一行的出发情况,c为经过这个桥的人数,可以提前算出。

直接转移是\(O(N^2M)\)的,加上一些类似前缀最小值优化的东西可以做到\(O(NM)\)

考虑继续发掘性质。
我们设出发地和目的地为关键点
容易看出,对于一行的某个关键点,它对整一行的答案影响可以写成一个斜率为-1的一次函数和一个斜率为1的一次函数,它显然是个斜率不降的函数(下凸壳)。

便于维护,我们对于每个j都记一个一次函数\(k_jx+b_j\),表示\(f[i][j-1]\)\(f[i][j]\)的连线的方程,显然交点处同时满足两个方程。

由于两个下凸的函数之和仍然是一个下凸的函数,因此只考虑关键点的影响时它总是个凸函数,我们只需要支持区间加一次函数即可。

考虑行间转移,\(F[i][j]=S[i][j]+min(F[i-1][k]+c\left|j-k\right|)\),\(S[i][j]\)就是关键点的贡献,我们只考虑\(min(F[i-1]k]+c\left|j-k\right|)\),我们找到一个最小的\(x_1\)满足\(k_{x_1+1}\geq -c\),最大的\(x_2\)满足\(k_{x_2}\leq c\)

\[ min(F[i-1][k]+c|k-x|)= \left\{ \begin{array}{ll} F[i-1][x_1]+c*x_1-c*x & x<x_1 \\ F[i-1][x] & x_1\leq x\leq x_2 \\ F[i-1][x_2]-c*x_2+c*x &x>x_2 \end{array}\right. \]

容易发现它还是个凸函数,相当于在原来的凸函数两边斜率绝对值大于c的部分修改掉。

这样我们只需要支持区间加、区间赋值为一次函数,以及查找某个斜率

线段树维护即可。
时间复杂度\(O(n\log n)\)

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 200005
#define LL long long
using namespace std;
int n,m,r,a[N][4],l,dc[N],le;
LL sum[N],ans,wz[N];

//lisanhua
struct node
{
    int x,y,p;
}d[N],ds[2][N];
bool cmp1(node x,node y)
{
    return x.y<y.y;
}
bool cmp2(node x,node y)
{
    return (x.x<y.x)||(x.x==y.x&&x.y<y.y);
}

//segment tree
#define M 400005
int t[M][2],n1;
LL sp[M][2],mxk[M],lz[M][2],lc[M][2]; 

bool bc[M];

void build(int k,int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    t[k][0]=++n1,build(t[k][0],l,mid);
    t[k][1]=++n1,build(t[k][1],mid+1,r);
}

int opx,opy;
LL opk,opb;

inline void upd(int k,LL &x,LL &y)
{
    sp[k][0]+=x,sp[k][1]+=y;
    lz[k][0]+=x,lz[k][1]+=y;
    mxk[k]+=x;
}

inline void upc(int k,LL &x,LL &y)
{
    lz[k][0]=lz[k][1]=0;
    sp[k][0]=x,sp[k][1]=y;
    lc[k][0]=x,lc[k][1]=y;
    mxk[k]=x;
    bc[k]=1;
}
inline void down(int k)
{
    if(bc[k]) 
    {
        upc(t[k][0],lc[k][0],lc[k][1]);
        upc(t[k][1],lc[k][0],lc[k][1]);
        lc[k][0]=lc[k][1]=0;bc[k]=0;
    }
    if(lz[k][0]||lz[k][1]) upd(t[k][0],lz[k][0],lz[k][1]),upd(t[k][1],lz[k][0],lz[k][1]);
    lz[k][0]=lz[k][1]=0;
}

inline void up(int k)
{
    mxk[k]=max(mxk[t[k][0]],mxk[t[k][1]]);
}

void add(int k,int l,int r)
{
    if(opx>opy||opx>r||opy<l) return;
    if(opx<=l&&r<=opy) upd(k,opk,opb);
    else
    {
        int mid=(l+r)>>1;down(k);
        add(t[k][0],l,mid),add(t[k][1],mid+1,r);
        up(k);
    }
}
void op_add(int p,int q,LL x,LL y) {opx=p,opy=q,opk=x,opb=y;add(1,1,r);}

void reset(int k,int l,int r)
{
    if(opx>opy||opx>r||opy<l) return;
    if(opx<=l&&r<=opy) upc(k,opk,opb);
    else
    {
        int mid=(l+r)>>1;down(k);
        reset(t[k][0],l,mid),reset(t[k][1],mid+1,r);
        up(k);
    }
}
void op_reset(int p,int q,LL x,LL y) {opx=p,opy=q,opk=x,opb=y;reset(1,1,r);}

int find(int k,int l,int r)
{
    if(l==r) return (sp[k][0]>=opk)?l:l+1;
    int mid=(l+r)>>1;down(k);
    return (mxk[t[k][0]]>=opk)?find(t[k][0],l,mid):find(t[k][1],mid+1,r);
}
int op_find(int x) {opk=x;return find(1,1,r);}

LL get(int k,int l,int r)
{
    if(l==r) return (sp[k][0]*wz[l]+sp[k][1]);
    int mid=(l+r)>>1;down(k);
    return(opx<=mid)?get(t[k][0],l,mid):get(t[k][1],mid+1,r);
}
LL op_get(int x) {opx=x;return get(1,1,r);}

LL smi;
void walk(int k,int l,int r)
{
    if(l==r) smi=min(smi,sp[k][0]*wz[l]+sp[k][1]);
    else
    {
        int mid=(l+r)>>1;down(k);
        walk(t[k][0],l,mid),walk(t[k][1],mid+1,r);
    }
}

//main 
int main()
{
    cin>>n>>m;
    ans=0,smi=1e18;
    fo(i,1,n)
    {
        scanf("%d%d%d%d",&a[i][0],&a[i][1],&a[i][2],&a[i][3]);
        if(a[i][0]>a[i][2]) swap(a[i][0],a[i][2]),swap(a[i][1],a[i][3]);
        if(a[i][0]==a[i][2]) ans+=abs(a[i][1]-a[i][3]);
        else
        {
            sum[a[i][0]+1]++,sum[a[i][2]]--;
            d[++l]=(node){a[i][0],a[i][1],0};
            d[++l]=(node){a[i][2],a[i][3],1};   
        }
    }
    fo(i,1,m+1) sum[i]=sum[i-1]+sum[i];
    
    sort(d+1,d+l+1,cmp1);
    fo(i,1,l)
    {
        if(i==1||d[i].y!=d[i-1].y) r++,wz[r]=d[i].y;
        dc[i]=r;
    }
    int lf[2]={0,0};
    fo(i,1,l) d[i].y=dc[i],ds[d[i].p][++lf[d[i].p]]=d[i];

    sort(ds[0]+1,ds[0]+lf[0]+1,cmp2);
    sort(ds[1]+1,ds[1]+lf[1]+1,cmp2);

    n1=1;
    r=max(r,1);
    build(1,1,r);
    int lx[2]={0,0};
    fo(p,0,1)
        for(lx[p]=1;lx[p]<=lf[p]&&ds[p][lx[p]].x<=1+p;lx[p]++) 
        {
            op_add(1,ds[p][lx[p]].y,-1,wz[ds[p][lx[p]].y]);
            op_add(ds[p][lx[p]].y+1,r,1,-wz[ds[p][lx[p]].y]);
        }

    fo(i,3,m+1)
    {
        int w=op_find(-sum[i-1])-1;
        op_reset(1,w,-sum[i-1],sum[i-1]*(LL)wz[w]+op_get(w));

        w=op_find(sum[i-1]+1)-1;
        op_reset(w+1,r,sum[i-1],-(LL)wz[w]*sum[i-1]+op_get(w));

        fo(p,0,1)
            for(;lx[p]<=lf[p]&&ds[p][lx[p]].x<=i-1+p;lx[p]++) 
            {
                op_add(1,ds[p][lx[p]].y,-1,wz[ds[p][lx[p]].y]);
                op_add(ds[p][lx[p]].y+1,r,1,-wz[ds[p][lx[p]].y]);
            }
    }
    walk(1,1,r);

    printf("%lld\n",ans+smi);
}

转载于:https://www.cnblogs.com/BAJimH/p/10574940.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34007020/article/details/93245034

智能推荐

java(数据库连接池)_java 多数据源连接池释放-程序员宅基地

文章浏览阅读438次。【代码】java(数据库连接池)_java 多数据源连接池释放

MOSFET热仿真_mosfet管在工作过程中的热性能仿真情况-程序员宅基地

文章浏览阅读1.7k次。功率电路中MOSFET的热研究原文:https://wenku.baidu.com/view/282066fa16fc700aba68fc16.html电源管理应用中的功率MOSFET的热分析方法原文:https://wenku.baidu.com/view/2c1b8bceda38376baf1fae78.html_mosfet管在工作过程中的热性能仿真情况

oracle新建用户、授权、修改密码、分配表空间、导入导出数据_oracle授权用户插入修改表-程序员宅基地

文章浏览阅读446次,点赞2次,收藏4次。oracle新建用户、授权、修改密码、分配表空间、导入导出数据**一**:在pl/sql中新建用户:一:在pl/sql中新建用户:创建用户: create user 用户名 identified by 密码; ;给用户授权:grant dba to 用户名;修改密码:alter user 用户名 identified by 新密码;创建表空间:create tablespace 表空间..._oracle授权用户插入修改表

Matlab 实验3《LSB与信息隐藏》_图像lsb清0-程序员宅基地

文章浏览阅读2k次。实验3《LSB与信息隐藏》3.1上机内容与要求3.1.1 写出“图像LSB清0”伪C语言描述的算法、原理与步骤;实验原始图像和不同位清0时载体图像的差别。3.1.2 写出“随机选取图像载体像素点,在LSB上信息隐藏嵌入过程和提取过程”原理与步骤;实验隐藏大小不同的信息时原始图像和载体图像的差别。3.2 实验过程分析3.2.1 写出“图像LSB清0”伪C语言描述的算法、原理与步骤;实验原始图像和不同位清0时载体图像的差别。Matlab语言:function data = shiyan3( )d_图像lsb清0

[ext4]空间管理 - 与分配相关的关键数据结构-程序员宅基地

文章浏览阅读173次。在块分配机制中,涉及到几个主要的数据结构。通过ext4_allocation_request描述块请求,然后基于块查找结果即上层需求来决定是否执行块分配操作。在分配过程中,为了更好执行分配,记录一些信息,需要对分配行为进行描述,就有结构体ext4_allocation_contex。在搜寻可用空间过程中,是有可能使用预分配空间的,因此还需要有能够描述预分..._ext4_num_b2c

001使用smokeping监控idc机房网络质量情况-程序员宅基地

文章浏览阅读80次。最近工作比较忙,也没有时间写博客,看到好友芮峰云最近一直在写博客,所以也手痒了,就先把之前的一些积累下来的文章分享给大家。本文是介绍如何的使用smokeping来监控idc机房的网络质量情况,从监控图上的延时与丢包能分辨出你机房的网络是否稳定,是否为多线,是否为BGP机房,到各城市的3个运行商网络各是什么情况,如果出现问题,如果有针对的解决。而且如果选择新机房的时候,你可以根据smokepin..._smokeping 延迟与丢包数值

随便推点

python pip 代理设置-程序员宅基地

文章浏览阅读682次。pip install --proxy="user:password@server:port" packagenameorigin url:http://xiuxixiuxi.blogspot.jp/2013/04/how-to-install-packages-with.htmlThere are two easy way to install packages for python (..._pip install --proxy=

Linux Deploy安装配置Ubuntu使用教程_linuxdeploy安装ubuntu-程序员宅基地

文章浏览阅读1.2w次,点赞6次,收藏44次。记录Linux Deploy使用总结1. 前言最近换了一部新手机,老的手机荣耀play也不能空着。正好平时电脑装了个虚拟机Ubuntu来做开发/运维环境,有点占电脑配置,无法一边开虚拟机一边玩游戏,老是不能放心玩耍。于是萌生起用手机做Linux服务器的想法。安卓是基于Linux内核进行开发的,理论上是可以实现Linux部署的。百度研究了下(研究了2天。。。),特此记录一下,本位以Ubuntu为..._linuxdeploy安装ubuntu

Freemarker使用详解_freemarker使用教程-程序员宅基地

文章浏览阅读9.4k次,点赞5次,收藏57次。1.什么是网页静态化技术随着用户访问量以及数据量的增大,网页静态化技术方案如今越来越流行。 什么是网页静态化技术呢?简单来说就是将网页以纯静态方式的形式展现。2.网页静态化技术与缓存技术的比较共同点:都可以减小数据库的访问压力。区别:(1)缓存技术适用于小规模的数据。以及一些经常变动的数据。(2)网页静态化技术适用于大规模但是变化不太频繁的数据。页面静态化与缓存技术的定义:页面静态化是指通过一些模板技术(如freemarker)将数据模型生成静态html页面并通过.._freemarker使用教程

mysql面试题分享-程序员宅基地

文章浏览阅读153次。一.基础笔试命令考察1. 开启MySQL服务service mysqld start/init.d/mysqld startsafe_mysql &关闭mysql服务service mysqld stop/etc/init.d/mysqld stopmysqladmin -uroot -p123456 shutdown2. 检测端口是否运行ls..._sql 语句分类及对应代表性关键字

Android隐藏系统状态栏(沉浸式状态栏)和设置状态栏颜色_android 隐藏状态栏-程序员宅基地

文章浏览阅读3.3k次。window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR__android 隐藏状态栏

Vitamio中文API文档(3)—— MediaController-程序员宅基地

文章浏览阅读67次。类概述 public classMediaControllerextendsFrameLayout 一个包含媒体播放器(MediaPlayer)的媒体控制条。通常包括“播放/暂停”和SeekBar。它管理MediaPlayer的状态以保持控件的同步。 使用这个类的方法: a). 通过编程来实例化这个类。 这个媒体控制器将创建一个具有默..._vitamio mediacontroller