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);
}

[JZOJ6075]【GDOI2019模拟2019.3.20】桥【DP】【线段树】的更多相关文章

  1. 省选模拟赛 4.26 T1 dp 线段树优化dp

    LINK:T1 算是一道中档题 考试的时候脑残了 不仅没写优化 连暴力都打挂了. 容易发现一个性质 那就是同一格子不会被两种以上的颜色染.(颜色就三种. 通过这个性质就可以进行dp了.先按照左端点排序 ...

  2. hdu 3016 dp+线段树

    Man Down Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  3. ZOJ 3349 Special Subsequence 简单DP + 线段树

    同 HDU 2836 只不过改成了求最长子串. DP+线段树单点修改+区间查最值. #include <cstdio> #include <cstring> #include ...

  4. 【BZOJ4071】八邻旁之桥(线段树)

    [BZOJ4071]八邻旁之桥(线段树) 题面 BZOJ权限题,洛谷链接 题解 既然\(k<=2\) 那么,突破口就在这里 分类讨论 ①\(k=1\) 这...不就是中位数吗.... 直接把所有 ...

  5. cf834D(dp+线段树区间最值,区间更新)

    题目链接: http://codeforces.com/contest/834/problem/D 题意: 每个数字代表一种颜色, 一个区间的美丽度为其中颜色的种数, 给出一个有 n 个元素的数组, ...

  6. Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)

    Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...

  7. 2019牛客暑期多校训练营(第一场)I dp+线段树

    题意 给出n个点,每个点有a,b两个属性,让你从左下角到右上角划一条线,线的左边每个点的贡献是\(a_i\),线的右边每个点的贡献是\(b_i\),使得两部分的总和最大. 分析 找一条折线将点分割开, ...

  8. [CSP-S模拟测试]:F(DP+线段树)

    题目传送门(内部题49) 输入格式 第一行四个整数$n,q,a,b$.接下来$n$行每行一个整数$p_i$. 输出格式 一行一个整数表示答案. 样例 样例输入: 10 3 3 7 样例输出: 数据范围 ...

  9. BZOJ 1835: [ZJOI2010]base 基站选址 [序列DP 线段树]

    1835: [ZJOI2010]base 基站选址 题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立 ...

随机推荐

  1. Java 设计模式系列(十七)中介者模式

    Java 设计模式系列(十七)中介者模式 用一个中介对象来封装一系列的对象交互.中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互 一.中介者模式结构 Media ...

  2. CoderForces 518C Anya and Smartphone (模拟)

    题意:给定一个手机,然后一共有 n 个app,告诉你每个屏幕最多放 k 个,现在要你运行 m 个app,每次都从第一个屏幕开始滑动,每运行一个,它就和前一个交换位置,第一个就不换了,现在问你要滑动多少 ...

  3. Uploadify多文件上传插件.NET使用案例+PHP使用案例

    ploadify是一个非常好用的多文件上传插件 插件下载:http://www.uploadify.com 下载后需要用到的文件: 接下来就是直接添加代码: Default.aspx代码 <%@ ...

  4. Java动态代理(三)——模拟AOP实现

    以下案例模拟AOP实现 目录结构 接口PersonService package com.ljq.service; public interface PersonService { public vo ...

  5. Android-XML与JSON的理解-JSON的数据格式

    据我了解,在多年以前浏览器客户端和服务器它们的通讯数据交互格式是XML, 使用XML来规定数据格式可读性确实非常强,XML的魅力确实很大,也很成熟,但是也有不足之处,就是在网络传输的时候对流量要求特别 ...

  6. PHP全栈学习笔记19

    thinkphp框架是一个免费的,开源,快速,简单的面向对象的轻量级PHP开发框架. 了解什么是thinkphp概述,thinkphp项目目录结构,thinkphp的控制器,视图,thinkphp项目 ...

  7. App与微信WebAPP

    我的App与微信搞上了 小麦积分墙摘自网络 最近有很多开发者关心的一个问题是如何提升app的下载量,透过现象开本质,app下载量的终极目标还是为多少客户提供了服务,抛开下载量KPI,app真心关心的问 ...

  8. mysql 删表引出的问题

    背景 将测试环境的表同步到另外一个数据库服务器中,但有些表里面数据巨大,(其实不同步该表的数据就行,当时没想太多),几千万的数据!! 步骤 1. 既然已经把数据同步过来的话,那就直接delete掉就行 ...

  9. bootstrap-table简单使用

    开发项目时总想着能不能有一款插件包含分页,查询等常用功能,后来发现了bootstrap-table 直接看代码和效果图 <!DOCTYPE html> <html lang=&quo ...

  10. jsp页面都放在web-inf下面说是要防止用户直接访问jsp页面,为么不能直接访问jsp

    为了保护那部分jsp页面,如果没有登录验证,那部分jsp用户可以直接访问,这样很不安全,放在WEB-INF下面,就使得只能WEB-INF文件夹外jsp页面调用里面的jsp,这样来使用,就比如我们有一个 ...