【BZOJ】栅栏-线段树优化DP
2018-03-19 23:08
447 查看
蒟蒻做了一天线段树,表示心态炸裂,从未想到线段树还有这么多操作(已狗带)。
传送门:BZOJ3387
终点在坐标原点(0,0),起点在(s,n)。如下所示,×为终点,s为起点。
奶牛们很笨拙,它们都是绕过栅栏而不是跳过去的。也就是说它们会沿着栅栏走直到走到栅栏头,然后向着x轴奔跑,直到碰到下一个栅栏拦住去路,然后再绕过去……
求奶牛从起点到终点需要走的最短水平距离。
-100000≤ai<bi≤100000,an≤s≤bn (ai,bi分别代表第i个栅栏的两个端点的横坐标)
于是自己yy了一种方法,居然依靠水数据得了七十分。
就是这样的愚蠢。
错因本蒟蒻已经写在注释里了,不过相信各位大佬看一眼就知道了(或许有的大佬不屑于看)。
下面讲讲正解。
对于从上到下走的时候,每走到一个新栅栏端点,找到最短距离有四种可能,分别是前一个和这一个左右互相组合。Dp方程就不写了,网上到处都有。
我们当然不会打n^2暴力dp。于是想一想(虽然我也没想出来),然后妙妙的事情就发生了!
我们是不是可以把每个已经处理过的dp值都存在一个线段树里,线段树的范围就是坐标范围(反正装得下),然而标记一下当前栅栏用0,1两个状态来分别贮存左右端点过来的最短路,取个最小值就好了。
所以这道题当然也有另一种做法,那就是dijkstra!
然而由于本蒟蒻太弱了,连dp做法都是抄题解还自行理解了一下午(然而还不是很懂)才懂了的,其他方法大家还是自行搜索吧。
传送门:BZOJ3387
题意
Farmer John 为奶牛们设置了一个障碍赛。障碍赛中有n个(n≤50000)各种长度的栅栏,每个都与x轴平行,其中的第i个栅栏的y坐标为i。终点在坐标原点(0,0),起点在(s,n)。如下所示,×为终点,s为起点。
奶牛们很笨拙,它们都是绕过栅栏而不是跳过去的。也就是说它们会沿着栅栏走直到走到栅栏头,然后向着x轴奔跑,直到碰到下一个栅栏拦住去路,然后再绕过去……
求奶牛从起点到终点需要走的最短水平距离。
数据范围
n≤50000 |s|≤100000-100000≤ai<bi≤100000,an≤s≤bn (ai,bi分别代表第i个栅栏的两个端点的横坐标)
题解
本蒟蒻怎么可能会做?orzzzzz于是自己yy了一种方法,居然依靠水数据得了七十分。
就是这样的愚蠢。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define INF 1e7 using namespace std; const int N=5e4+10; int n,s,ans; struct P{ int x; bool in; }t[N<<2]; int fr,to; inline int read() { char c=getchar();int x=0,t=1; while(c<'0' || c>'9') {if(c=='-') t=-1;c=getchar();} while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*t; } bool cmp(P A,P B) { return A.x<B.x; } inline int bs(int x){ return x>0? x:-x; } inline int min(int a,int b) { return a>b? b:a; } int main(){ n=read();s=read();ans=INF; for(int i=0;i<n;i++){ t[i<<1].x=read();t[i<<1].in=true; t[i<<1|1].x=read();t[i<<1|1].in=false; }n<<=1; int now=0; sort(t,t+n,cmp); for(int i=0;i<n;i++){ if(t[i].in){ now++; if(now==1){ if(t[i].x>s) break; fr=t[i].x; } }else{ now--; if(now==0){ if(t[i].x<s) continue; to=t[i].x; ans=min(bs(s-fr)+bs(fr),ans); ans=min(bs(s-to)+bs(to),ans); } } } if(ans==INF){ printf("%d\n",bs(s)); }else printf("%d\n",ans); return 0; } //a logical problem: //it is actually not necessary for us to go to the farthest point //so sometimes the answer may be larger than expected //somestimes think so little and make problem more silly
错因本蒟蒻已经写在注释里了,不过相信各位大佬看一眼就知道了(或许有的大佬不屑于看)。
下面讲讲正解。
对于从上到下走的时候,每走到一个新栅栏端点,找到最短距离有四种可能,分别是前一个和这一个左右互相组合。Dp方程就不写了,网上到处都有。
我们当然不会打n^2暴力dp。于是想一想(虽然我也没想出来),然后妙妙的事情就发生了!
我们是不是可以把每个已经处理过的dp值都存在一个线段树里,线段树的范围就是坐标范围(反正装得下),然而标记一下当前栅栏用0,1两个状态来分别贮存左右端点过来的最短路,取个最小值就好了。
所以这道题当然也有另一种做法,那就是dijkstra!
然而由于本蒟蒻太弱了,连dp做法都是抄题解还自行理解了一下午(然而还不是很懂)才懂了的,其他方法大家还是自行搜索吧。
代码
#include <cstdio> #include <iostream> #include <cstring> #define lson x<<1 #define rson x<<1|1 using namespace std; const int maxn=200005; int n,m; int s[maxn<<2][2],t[maxn<<2][2];//s->min t->1(covered) 2->uncovered void pushdown(int x,int p) { if(t[x][p]) { s[lson][p]=s[rson][p]=1<<30; t[lson][p]=t[rson][p]=1; t[x][p]=0; } } void updata(int l,int r,int x,int a,int b,int p) { if(l==r) { s[x][p]=min(s[x][p],b); return ; } pushdown(x,p); int mid=l+r>>1; if(a<=mid) updata(l,mid,lson,a,b,p); else updata(mid+1,r,rson,a,b,p); s[x][p]=min(s[lson][p],s[rson][p]); } void cover(int l,int r,int x,int a,int b,int p) { if(a<=l&&r<=b) { t[x][p]=1,s[x][p]=1<<30; return ; } pushdown(x,p); int mid=l+r>>1; if(a<=mid) cover(l,mid,lson,a,b,p); if(b>mid) cover(mid+1,r,rson,a,b,p); s[x][p]=min(s[lson][p],s[rson][p]); } int query(int l,int r,int x,int a,int b,int p) { if(a<=l&&r<=b) return s[x][p]; pushdown(x,p); int mid=l+r>>1; if(b<=mid) return query(l,mid,lson,a,b,p); if(a>mid) return query(mid+1,r,rson,a,b,p); return min(query(l,mid,lson,a,b,p),query(mid+1,r,rson,a,b,p)); } int main() { scanf("%d%d",&n,&m); m+=100002; memset(s,0x3f,sizeof(s)); updata(1,maxn,1,100002,-100002,0),updata(1,maxn,1,100002,100002,1);//初始化0 int i,a,b,ta,tb; for(i=1;i<=n;i++) { scanf("%d%d",&a,&b); a+=100002,b+=100002; ta=min(query(1,maxn,1,1,a,0)+a,query(1,maxn,1,a+1,maxn,1)-a); tb=min(query(1,maxn,1,1,b,0)+b,query(1,maxn,1,b+1,maxn,1)-b); updata(1,maxn,1,a,ta-a,0),updata(1,maxn,1,a,ta+a,1); updata(1,maxn,1,b,tb-b,0),updata(1,maxn,1,b,tb+b,1); if(b>a+1) cover(1,maxn,1,a+1,b-1,0),cover(1,maxn,1,a+1,b-1,1); } printf("%d",min(query(1,maxn,1,1,m,0)+m,query(1,maxn,1,m+1,maxn,1)-m)); return 0; }
相关文章推荐
- bzoj 1835 基站选址(线段树优化Dp)
- bzoj 4712: 洪水 树链剖分+线段树优化dp
- bzoj 1835: [ZJOI2010]base 基站选址 线段树优化dp
- bzoj 1835: [ZJOI2010]base 基站选址(线段树优化dp)
- #bzoj3380#小Q的新玩具(DP + set优化 / 线段树优化)
- 【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp
- bzoj 3711: [PA2014]Druzyny 分治+线段树优化dp
- bzoj 3790: 神奇项链 (manacher+线段树优化DP)
- [线段树优化 DP] BZOJ 3242 [Noi2013]快餐店
- BZOJ 1835 基站选址(线段树优化DP)
- bzoj1835(线段树优化dp)
- [BZOJ 1563][NOI 2009]诗人小G(四边形优化DP)
- ZOJ 3349 Special Subsequence(线段树优化DP)
- _bzoj1911 [Apio2010]特别行动队【斜率优化dp】
- bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp
- bzoj 1096 斜率优化DP
- 【BZOJ1096】仓库建设,斜率优化DP练习
- [后缀自动机][单调队列优化DP] BZOJ 2806: [Ctsc2012]Cheat
- bzoj2442[Usaco2011 Open]修剪草坪 单调队列优化dp
- bzoj 1600: [Usaco2008 Oct]建造栅栏(DP)