【bzoj3387-跨栏训练】线段树+dp

我们可以想到一个dp方程:f[i][0]表示当前在i个栅栏的左端点,f[i][1]表示在右端点。

分两种情况:

第一种:假设现在要更新线段gh的左端点g,而它下来的路径被ef挡住了,那么必定是有ef来更新g。

为什么呢?因为其它点走到g必定要下落,比如说d到g,就相当于d到f再到g。

第二种:假设到ab的路径上没有东西挡着,那就可以直接从源点走过去再直接下落。

按照从上到下的顺序插入线段,线段树就是找当前的某个点被哪条id最大(也就是最低的)线段所覆盖。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<ctime>
  6 #include<queue>
  7 #include<algorithm>
  8 using namespace std;
  9
 10 const int N=2*2*50010,INF=(int)1e9;
 11 int n,st,pl,tl,r[N],f[N][2];
 12 struct node{
 13     int d,id,tmp;
 14 }p[N];
 15 struct nd{
 16     int x1,x2;
 17 }a[N];
 18 struct trnode{
 19     int l,r,lc,rc,id,lazy;
 20 }t[2*N];
 21
 22 bool cmp_d(node x,node y){return x.d<y.d;}
 23 int myabs(int x){return x>0 ? x:-x;}
 24 int minn(int x,int y){return x<y ? x:y;}
 25 int maxx(int x,int y){return x>y ? x:y;}
 26
 27 int bt(int l,int r)
 28 {
 29     int x=++tl;
 30     t[x].l=l;t[x].r=r;
 31     t[x].lc=t[x].rc=0;
 32     t[x].id=INF;t[x].lazy=INF;
 33     if(l<r)
 34     {
 35         int mid=(l+r)/2;
 36         t[x].lc=bt(l,mid);
 37         t[x].rc=bt(mid+1,r);
 38     }
 39     return x;
 40 }
 41
 42 void upd(int x)
 43 {
 44     if(t[x].lazy==INF) return ;
 45     int id=t[x].lazy,lc=t[x].lc,rc=t[x].rc;
 46     t[x].lazy=INF;
 47     t[x].id=minn(t[x].id,id);
 48     if(lc) t[lc].lazy=minn(t[lc].lazy,id);
 49     if(rc) t[rc].lazy=minn(t[rc].lazy,id);
 50 }
 51
 52 void change(int x,int l,int r,int id)
 53 {
 54     upd(x);
 55     if(t[x].l==l && t[x].r==r)
 56     {
 57         t[x].lazy=minn(t[x].lazy,id);
 58         upd(x);
 59         return ;
 60     }
 61     int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
 62     if(r<=mid) change(lc,l,r,id);
 63     else if(l>mid) change(rc,l,r,id);
 64     else
 65     {
 66         change(lc,l,mid,id);
 67         change(rc,mid+1,r,id);
 68     }
 69 }
 70
 71 int query(int x,int p)
 72 {
 73     upd(x);
 74     if(t[x].l==t[x].r) return t[x].id;
 75     int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
 76     if(p<=mid) return query(lc,p);
 77     else return query(rc,p);
 78 }
 79
 80 int main()
 81 {
 82     // freopen("a.in","r",stdin);
 83     // freopen("me.out","w",stdout);
 84     freopen("obstacle.in","r",stdin);
 85     freopen("obstacle.out","w",stdout);
 86     scanf("%d%d",&n,&st);
 87     int x,ed;ed=0;pl=0;tl=0;
 88     p[++pl].d=st;p[pl].id=n+1;
 89     p[++pl].d=ed;p[pl].id=n+2;
 90     for(int i=1;i<=n;i++)
 91     {
 92         scanf("%d%d",&a[i].x1,&a[i].x2);
 93         if(a[i].x1>a[i].x2) swap(a[i].x1,a[i].x2);
 94         p[++pl].d=a[i].x1;p[pl].id=i;p[pl].tmp=0;
 95         p[++pl].d=a[i].x2;p[pl].id=i;p[pl].tmp=1;
 96     }
 97     sort(p+1,p+1+pl,cmp_d);
 98     int mx=0;p[0].d=INF;
 99     for(int i=1;i<=pl;i++)
100     {
101         if(p[i].d!=p[i-1].d) mx++,r[mx]=p[i].d;
102         if(p[i].id==n+1) st=mx;
103         else if(p[i].id==n+2) ed=mx;
104         else
105         {
106             if(p[i].tmp==0) a[p[i].id].x1=mx;
107             else a[p[i].id].x2=mx;
108         }
109     }
110     bt(1,mx);
111     change(1,st,st,n+1);
112     a[n+1].x1=a[n+1].x2=st;
113     f[n+1][0]=f[n+1][1]=0;
114     a[0].x1=a[0].x2=ed;
115     for(int i=n;i>=0;i--)
116     {
117         x=query(1,a[i].x1);
118         if(x<INF) f[i][0]=minn(f[x][0]+myabs(r[a[x].x1]-r[a[i].x1]),f[x][1]+myabs(r[a[x].x2]-r[a[i].x1]));
119         else f[i][0]=myabs(r[st]-r[a[i].x1]);
120         x=query(1,a[i].x2);
121         if(x<INF) f[i][1]=minn(f[x][0]+myabs(r[a[x].x1]-r[a[i].x2]),f[x][1]+myabs(r[a[x].x2]-r[a[i].x2]));
122         else f[i][1]=myabs(r[st]-r[a[i].x2]);
123         change(1,a[i].x1,a[i].x2,i);
124     }
125     printf("%d\n",f[0][0]);
126     return 0;
127 }
时间: 2024-10-06 16:26:36

【bzoj3387-跨栏训练】线段树+dp的相关文章

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[

CF932F(李超线段树+dp)

CF932F(李超线段树+dp) 此题又是新玩法, 李超线段树合并优化\(dp\) 一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价 \(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\) 如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

hdu4521(线段树+dp)

传送门:小明系列问题——小明序列 题意:有n个数,求间距大于d的最长上升序列. 分析:dp[i]表示在i点以a[i]结束距离大于d的最长上升序列,然后每更新到第i点时,取i-d之前小于a[i]的数为结束的最长上升序列进行状态转移,并维护前i-d之前的最大上升序列,维护i-d之前的每点为结束的最长上升序列用线段树维护即可. #pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #inc

lightoj1085 线段树+dp

1 //Accepted 7552 KB 844 ms 2 //dp[i]=sum(dp[j])+1 j<i && a[j]<a[i] 3 //可以用线段树求所用小于a[i]的dp[j]的和 4 //需要离散化 5 #include <cstdio> 6 #include <cstring> 7 #include <iostream> 8 #include <queue> 9 #include <cmath> 10 #

codeforces#426(div1) B - The Bakery (线段树 + dp)

题意:把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和. 思路: dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1) dp[i][j]表示第 i 个数分到第 j 段的最大值. v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1. 此时时间复杂度 O(n*m*log(n)). 线