bzoj3387-跨栏训练线段树+dp
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
以上是关于bzoj3387-跨栏训练线段树+dp的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)4
bzoj5123[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索