一个重要的结论是,如果在可以流的木板之间连一条有向边的话,边的条数不会很多,而且这是一个DAG
所以如果已经处理出了图,就在DAG上跑dp就可以了
关键是怎么建图
一个好的思路是按照端点次序进行访问,左端点插入set,右端点删除set,set内按照高度排序,这样在set里进行lower_bound就能找出被当前木板隔开的两块木板,把这两块木板之间的边删掉,并分别与中间木板连边
要注意设置INF的时候要设成2e9而不是1e9
说的有点抽象,看代码吧
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;
const int MOD=1e9+7;
const int INF=1e9;
const int magic=348;
struct node
{
int h,l,r;
int num;
}a[100048];
Pair e[200048];int top=0;
int d[200048];queue<int> q;
int seq[200048],stop=0;
int dp[200048];
int n,t;
set<Pair> m,s;
set<Pair>::iterator iter;
int head[200048],nxt[400048],to[400048],f[400048],tot=1;
inline void addedge(int s,int t,int len)
{
to[++tot]=t;nxt[tot]=head[s];head[s]=tot;f[tot]=len;
d[t]++;
}
int main ()
{
int i,j,up,down,x,y;
scanf("%d%d",&n,&t);
for (i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i].h,&a[i].l,&a[i].r);
a[i].num=i;
}
a[n+1].h=0;a[n+1].l=-INF;a[n+1].r=INF;
a[n+2].h=t;a[n+2].l=-INF;a[n+2].r=INF;
for (i=1;i<=n;i++)
{
e[++top].x=a[i].l;
e[top].y=i;
e[++top].x=a[i].r;
e[top].y=-i;
}
sort(e+1,e+top+1);
s.insert(mp(0,n+1));
s.insert(mp(t,n+2));
m.insert(mp(n+1,n+2));
for (i=1;i<=top;i++)
{
if (e[i].y<0)
{
s.erase(mp(a[-e[i].y].h,-e[i].y));
continue;
}
iter=s.lower_bound(mp(a[e[i].y].h,e[i].y));
up=iter->y;
down=(--iter)->y;
m.erase(mp(down,up));
m.insert(mp(down,e[i].y));
m.insert(mp(e[i].y,up));
s.insert(mp(a[e[i].y].h,e[i].y));
}
int l1,l2,r1,r2,flow;
for (iter=m.begin();iter!=m.end();iter++)
{
l1=a[iter->x].l;l2=a[iter->y].l;
r1=a[iter->x].r;r2=a[iter->y].r;
flow=min(r1,r2)-max(l1,l2);
addedge(iter->x,iter->y,flow);
}
for (i=1;i<=n+2;i++) if (!d[i]) q.push(i);
while (!q.empty())
{
x=q.front();q.pop();
seq[++stop]=x;
for (i=head[x];i;i=nxt[i])
{
y=to[i];
d[y]--;
if (!d[y]) q.push(y);
}
}
dp[n+1]=2*INF;
for (i=1;i<=stop;i++)
{
x=seq[i];
for (j=head[x];j;j=nxt[j])
{
y=to[j];
dp[y]=max(dp[y],min(f[j],dp[x]));
}
}
printf("%d\n",dp[n+2]);
return 0;
}
这题还有一种线段树的做法
先将木板的左右端点离散化,再将所有的木板按照高度排序,每处理一块木板后就将这个区间染色,染成木板编号
对于一块木板,在他的左右端点处找最大值,即可找到离它最近的木板,再查询这两块木板重叠处的最大值是否是较小木板的编号,以确定两块木板之间是否有其他木板
要考虑到如果一块很长的木板在上方覆盖住了下方的一块短木板,那么长木板的左右端点都将扫不到短木板,所以要将木板数组reverse以后再进行一轮相同的操作
用线段树连好边以后就在DAG上跑dp就可以了,与方法一无异
这种方法常数略大,而且比较容易出错(惨)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <utility>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <deque>
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define LL long long
#define Pair pair<int,int>
#define LOWBIT(x) x & (-x)
using namespace std;
const int MOD=1e9+7;
const int INF=1e9;
const int magic=348;
int head[200048],nxt[400048],to[400048],f[400048],tot=1;
inline void addedge(int s,int t,int len)
{
to[++tot]=t;nxt[tot]=head[s];head[s]=tot;f[tot]=len;
}
struct node
{
int left,right;
int maxn;
bool f;
}tree[600048];
struct node1
{
int h,l,r;
int ll,rr;
}a[200048];
int n,t;
set<pair<int,pair<int,bool> > > s;
set<pair<int,pair<int,bool> > >::iterator iter;
int dp[400048];
bool cmp(node1 x,node1 y)
{
return x.h<y.h;
}
void build(int cur,int left,int right)
{
tree[cur].left=left;tree[cur].right=right;tree[cur].maxn=-1;
tree[cur].f=false;
if (left+1<right)
{
int mid=(left+right)>>1;
build(cur*2,left,mid);
build(cur*2+1,mid,right);
}
}
int query(int cur,int left,int right)
{
if (tree[cur].left+1==tree[cur].right) return tree[cur].maxn;
if (tree[cur].f) return tree[cur].maxn;
int res1=-1,res2=-1,mid=(tree[cur].left+tree[cur].right)>>1;
if (left<mid) res1=query(cur*2,left,right);
if (mid<right) res2=query(cur*2+1,left,right);
return max(res1,res2);
}
void update(int cur,int left,int right,int val)
{
if (left<=tree[cur].left && tree[cur].right<=right)
{
tree[cur].f=true;
tree[cur].maxn=val;
return;
}
if (tree[cur].f)
{
tree[cur].f=false;
tree[cur*2].f=true;tree[cur*2].maxn=tree[cur].maxn;
tree[cur*2+1].f=true;tree[cur*2+1].maxn=tree[cur].maxn;
}
int mid=(tree[cur].left+tree[cur].right)>>1;
if (left<mid) update(cur*2,left,right,val);
if (mid<right) update(cur*2+1,left,right,val);
}
bool check(int x,int y)
{
int left,right;
left=max(a[x].ll,a[y].ll);right=min(a[x].rr,a[y].rr);
if (query(1,left,right)==x) return true; else return false;
}
int main ()
{
int i,j,xx,y,ff,x;
scanf("%d%d",&n,&t);
for (i=1;i<=n;i++) scanf("%d%d%d",&a[i].h,&a[i].l,&a[i].r);
a[n+1].h=0;a[n+1].l=-INF;a[n+1].r=INF;
a[n+2].h=t;a[n+2].l=-INF;a[n+2].r=INF;
n+=2;
sort(a+1,a+n+1,cmp);
for (i=1;i<=n;i++)
{
s.insert(mp(a[i].l,mp(i,false)));
s.insert(mp(a[i].r,mp(i,true)));
}
xx=1;
iter=s.begin();
int last=iter->x;
if (!iter->y.y) a[iter->y.x].ll=1; else a[iter->y.x].rr=1;
iter++;
for (;iter!=s.end();iter++)
{
if (!iter->y.y)
a[iter->y.x].ll=iter->x==last?xx:++xx;
else
a[iter->y.x].rr=iter->x==last?xx:++xx;
last=iter->x;
}
build(1,1,xx+10);
for (i=1;i<=n;i++)
{
y=i;
x=query(1,a[i].ll,a[i].ll+1);
if (check(x,y))
{
ff=min(a[x].r,a[y].r)-max(a[x].l,a[y].l);
addedge(x,y,ff);
}
x=query(1,a[i].rr-1,a[i].rr);
if (check(x,y))
{
ff=min(a[x].r,a[y].r)-max(a[x].l,a[y].l);
addedge(x,y,ff);
}
update(1,a[i].ll,a[i].rr,i);
}
reverse(a+1,a+n+1);
for (i=1;i<=n;i++)
{
y=i;
x=query(1,a[i].ll,a[i].ll+1);
if (check(x,y))
{
ff=min(a[x].r,a[y].r)-max(a[x].l,a[y].l);
addedge(n+1-y,n+1-x,ff);
}
x=query(1,a[i].rr-1,a[i].rr);
if (check(x,y))
{
ff=min(a[x].r,a[y].r)-max(a[x].l,a[y].l);
addedge(n+1-y,n+1-x,ff);
}
update(1,a[i].ll,a[i].rr,i);
}
reverse(a+1,a+n+1);
dp[1]=2*INF;
for (i=1;i<=n-1;i++)
for (j=head[i];j;j=nxt[j])
{
y=to[j];
dp[y]=max(dp[y],min(dp[i],f[j]));
}
printf("%d\n",dp[n]);
return 0;
}