您的位置:首页 > 其它

【BZOJ】栅栏-线段树优化DP

2018-03-19 23:08 447 查看
蒟蒻做了一天线段树,表示心态炸裂,从未想到线段树还有这么多操作(已狗带)。

传送门: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: