[BZOJ]4200: [Noi2015]小园丁与老司机
Time Limit: 20 Sec Memory Limit: 512 MBSec Special Judge
Description
小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面。田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤i≤n1≤i≤n) 位于坐标 (xi,yi)(xi,yi)。任意两棵树的坐标均不相同。
老司机 Mr. P 从原点 (0,0)(0,0) 驾车出发,进行若干轮行动。每一轮,Mr. P 首先选择任意一个满足以下条件的方向:
为左、右、上、左上 45∘45∘ 、右上 45∘45∘ 五个方向之一。
沿此方向前进可以到达一棵他尚未许愿过的树。
完成选择后,Mr. P 沿该方向直线前进,必须到达该方向上距离最近的尚未许愿的树,在树下许愿并继续下一轮行动。如果没有满足条件的方向可供选择,则停止行动。他会采取最优策略,在尽可能多的树下许愿。若最优策略不唯一,可以选择任意一种。
不幸的是,小园丁 Mr. S 发现由于田野土质松软,老司机 Mr. P 的小汽车在每轮行进过程中,都会在田野上留下一条车辙印,一条车辙印可看作以两棵树(或原点和一棵树)为端点的一条线段。
在 Mr. P 之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像 Mr. P 一样任选一种最优策略行动。Mr. S 认为非左右方向(即上、左上 45∘45∘ 、右上 45∘45∘ 三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有“可能留下非左右方向车辙印”的地面。
“可能留下非左右方向车辙印”的地面应当是田野上的若干条线段,其中每条线段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:
从原点或任意一棵树出发。
只能向上、左上 45∘45∘ 、右上 45∘45∘ 三个方向之一移动,并且只能在树下改变方向或停止。
只能经过“可能留下非左右方向车辙印”的地面,但是同一块地面可以被多台轧路机经过。
现在 Mr. P 和 Mr. S 分别向你提出了一个问题:
请给 Mr .P 指出任意一条最优路线。
请告诉 Mr. S 最少需要租用多少台轧路机。
Input
输入文件的第 1 行包含 1 个正整数 n,表示许愿树的数量。
接下来 n 行,第 i+1 行包含 2个整数 xi,yi,中间用单个空格隔开,表示第 i 棵许愿树的坐标。
Output
输出文件包括 3 行。
输出文件的第 1 行输出 1 个整数 m,表示 Mr. P 最多能在多少棵树下许愿。
输出文件的第 2 行输出 m 个整数,相邻整数之间用单个空格隔开,表示 Mr. P 应该依次在哪些树下许愿。
输出文件的第 3 行输出 1 个整数,表示 Mr. S 最少需要租用多少台轧路机。
Sample Input
6
-1 1
1 1
-2 2
0 8
0 9
0 10
-1 1
1 1
-2 2
0 8
0 9
0 10
Sample Output
3
2 1 3
3
Hint
explanation
最优路线 2 条可许愿 3 次:(0,0)→(1,1)→(−1,1)→(−2,2)(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。 至少 3 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)(−1,1)→(−2,2) 和 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。
2 1 3
3
Hint
explanation最优路线 2 条可许愿 3 次:(0,0)→(1,1)→(−1,1)→(−2,2)(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。 至少 3 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)(−1,1)→(−2,2) 和 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。
Solution
把坐标分别按x,y,x+y,x-y排序即可求出一个点向各个方向能到的点,然后DP求出从原点到一个点最多经过的点数,状态有两种情况,一种是从y坐标小的转移过来,一种是从y坐标相同的转移过来,两种最好分开考虑,细节可能有点多。DP完求出转移路径即可解决第一问,第二问要我们求出最少用几条链才能覆盖所有可能在最优路径上的y坐标增大的边,求出这些边后我们可以用带下界的最小流解决(理解最小流后建图比较容易)。Solution
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; inline int read() { int x,f=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')f=0; for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return f?x:-x; } #define MN 50000 #define MV 50002 #define ME 250000 #define S MV+1 #define T MV+2 #define INF 0x3FFFFFFF struct node{int x,y,id;}p[MN+5]; bool cmp1(const node&a,const node&b){return a.x+a.y==b.x+b.y?a.y<b.y:a.x+a.y<b.x+b.y;} bool cmp2(const node&a,const node&b){return a.x-a.y==b.x-b.y?a.y<b.y:a.x-a.y<b.x-b.y;} bool cmp3(const node&a,const node&b){return a.x==b.x?a.y<b.y:a.x<b.x;} bool cmp4(const node&a,const node&b){return a.y==b.y?a.x<b.x:a.y<b.y;} int s[MN+5],t1[MN+5],t2[MN+5],t3[MN+5],l[MN+5],r[MN+5],f[MN+5],ff[MN+5],lf[MN+5],rf[MN+5],a[MN+5],an; vector<int> v,t[MN+5]; namespace maxFlow { struct edge{int nx,t,w;}e[ME*2+5]; int h[MV+5],en=1,d[MV+5],c[MV+5],q[MV+5],qn; inline void ins(int x,int y,int w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,0};h[y]=en; } bool bfs() { int i,j; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx) if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; return d[T]; } int dfs(int x,int r) { if(x==T)return r; int k,u=0; for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1) { k=dfs(e[i].t,min(e[i].w,r-u)); u+=k;e[i].w-=k;e[i^1].w+=k; if(u==r)return u; } return d[x]=0,u; } int dinic(){int res=0;while(bfs())res+=dfs(S,INF);return res;} } void upd(int x,int y) { if(ff[y]+1>f[x])f[x]=ff[y]+1,t[x].clear(); if(ff[y]+1==f[x])t[x].push_back(y); } void get_ans(int x) { a[++an]=x; int i,j,u=1; if(f[x]==ff[x]){u=0;if(p[x].id)get_ans(t[x][0]);} for(i=l[x];u&&i<x;++i)if(f[i]+x-l[x]==ff[x]) { u=0; for(j=x;--j>i;)a[++an]=j; for(j=l[x];j<=i;++j)a[++an]=j; if(p[i].id)get_ans(t[i][0]); } for(i=r[x];u&&i>x;--i)if(f[i]+r[x]-x==ff[x]) { u=0; for(j=x;++j<i;)a[++an]=j; for(j=r[x];j>=i;--j)a[++an]=j; if(p[i].id)get_ans(t[i][0]); } } int d[MN+5],u[MN+5],c[MN+5]; inline void ins(int x,int y){maxFlow::ins(x,y,INF);++c[x];--c[y];} void dfs(int x) { if(d[x])return; int i,j;d[x]=1; if(f[x]==ff[x]&&!u[x])for(u[x]=1,i=0;i<t[x].size();++i)ins(t[x][i],x),dfs(t[x][i]); for(i=l[x];i<x;++i)if(f[i]+x-l[x]==ff[x]&&!u[i])for(u[i]=1,j=0;j<t[i].size();++j)ins(t[i][j],i),dfs(t[i][j]); for(i=r[x];i>x;--i)if(f[i]+r[x]-x==ff[x]&&!u[i])for(u[i]=1,j=0;j<t[i].size();++j)ins(t[i][j],i),dfs(t[i][j]); } int main() { int n=read(),i,j,ans=0; for(i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].id=i; sort(p,p+n+1,cmp1); for(i=0;i<n;++i)if(p[i].x+p[i].y==p[i+1].x+p[i+1].y)t1[p[i].id]=p[i+1].id; sort(p,p+n+1,cmp2); for(i=0;i<n;++i)if(p[i].x-p[i].y==p[i+1].x-p[i+1].y)t2[p[i].id]=p[i+1].id; sort(p,p+n+1,cmp3); for(i=0;i<n;++i)if(p[i].x==p[i+1].x)t3[p[i].id]=p[i+1].id; sort(p,p+n+1,cmp4); for(i=0;i<=n;++i)s[p[i].id]=i; memset(f,200,sizeof(f));f[s[0]]=0; for(i=0;i<=n;i=r[i]+1) { for(l[i]=r[i]=i;r[i]<n&&p[r[i]+1].y==p[r[i]].y;++r[i]); for(j=l[i];++j<=r[i];)l[j]=l[i],r[j]=r[i]; for(lf[l[i]]=-INF,j=l[i];++j<=r[i];)lf[j]=max(lf[j-1],f[j-1]); for(rf[r[i]]=-INF,j=r[i];--j>=l[i];)rf[j]=max(rf[j+1],f[j+1]); for(j=l[i];j<=r[i];++j) { ff[j]=max(f[j],max(lf[j]+j-l[i],rf[j]+r[i]-j)); if(t1[p[j].id])upd(s[t1[p[j].id]],j); if(t2[p[j].id])upd(s[t2[p[j].id]],j); if(t3[p[j].id])upd(s[t3[p[j].id]],j); if(ff[j]>ans)ans=ff[j],v.clear(); if(ff[j]==ans)v.push_back(j); } } printf("%d\n",ans); get_ans(v[0]); for(i=an;--i;)printf("%d ",p[a[i]].id);puts(""); for(i=0;i<v.size();++i)dfs(v[i]); for(i=0;i<=n;++i) { if(c[i]<0)maxFlow::ins(S,i,-c[i]);else maxFlow::ins(i,T,c[i]); maxFlow::ins(MN+1,i,INF);maxFlow::ins(i,MN+2,INF); } maxFlow::dinic(); maxFlow::ins(MN+2,MN+1,INF); printf("%d",maxFlow::dinic()); }