4200: [Noi2015]小园丁与老司机

Time Limit: 20 Sec  Memory Limit: 512 MBSec  Special Judge
Submit: 139  Solved: 80
[Submit][Status][Discuss]

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

Sample Output

3
2 1 3
3

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)。

HINT

Source

[Submit][Status][Discuss]



dp + 有上下界的最小流,细节非常非常非常多。。。。。搞了整整一上午。。。

先考虑前两问,,老司机的开车方向只能向纵坐标增大,或者是左右开,因而可以分开dp

定义g[i]:第i个点,由y小于yi的点转移而来,最大值

那么对于每个i,从下往上走到i的方向只有三个,找出这三个方向上距离i最近的点,直接转移就行了

定义f[i]:第i个点,由左边或者右边开过来的最大值

对于f[i]的转移,首先让所有可能的路径开到当前这一层,也就是处理好该层所有的g[i]

贪心地想,如果要从同层的i开到j,假设i在j左边,那么最好的方案,就是i先开到最左端,然后慢慢往右开

反过来也是一样的,因此对于每层的f[i],从左往右从右往左分别用g[i]更新一次就行了

因而对于g[i]的转移,其实是要同时考虑三个方向上的点的f[i]和g[i]

最后检查一下所有点的f[i],g[i],就能得出第一问了。

现在要找出路径,任取一个合法的终点,每次判断它是属于哪一类(同层转移或者下层往上得到)

然后在符合的转移集合直接枚举上一个点,再继续搜索,要注意同层转移的时候把中间点加入

这样就解决了前两问,对于第三问,建模如下

倒着dp一次,和找路径的方式一样,处理出所有可能成为最短路径上的边,同层边忽略

对于从下往上的边(i,j),从i向j连一条下界为1,上界为INF的边

对于所有可能在最短路上的点,从s到它连一条INF的边,它到t连一条INF的边

也就是说,可以无限从这个点进入,无限在这个点停止

对于该网络的最小可行流就是第三问的答案

怎么求最小流?在网上搜了很久很久,,确定了时间复杂度低又好实现的模板

先构造成循环流,但先不添加(t,s,INF)这条边

然后跑一遍超级源汇最大流,再添加(t,s,INF),跑第二遍超级源汇最大流,这次跑出的最大流就是原图最小流了


最后,,此题细节很多。。

网上看到有人实现的时候,说题目给定每层的点不超过1k,因而每层直接O(N^2)暴力找路

然后又说只T第7个点。其实第7个点根本没有每层不超过1k点的限制的

因此,我处理的时候,是开一个vis数组,判断哪些点有没有被加入最短路集合

这样dp就可以写成BFS的模式,但是不能同时找随机路径(因为路径的点可能在入集合前就被经过了)


总之码农题都是好麻烦的。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;const int maxn = 5E4 + 50;
const int maxm = 2E6 + 20;
const int INF = ~0U>>1;struct E{int to,cap,flow; E(){}E(int to,int cap,int flow): to(to),cap(cap),flow(flow){}
}edgs[maxm];struct P{int x,y; P(){}P(int x,int y): x(x),y(y){}
}Point[maxn];struct data{int Num,typ; data(){}data(int Num,int typ): Num(Num),typ(typ){}
};struct d2{int x,y; d2(){}d2(int x,int y): x(x),y(y){}bool operator < (const d2 &B) const {if (x < B.x) return 1;if (x > B.x) return 0;return y < B.y;}
};int n,cur = 1,cnt,tp,s,t,S,T,sum,Ans,a[maxn],in[maxn],out[maxn],Cur[maxn],f[maxn],g[maxn],pos[maxn],Road[maxn],L[maxn];
bool in_road[maxn],vis[maxn][2],Reach[maxn][2],Typ[maxn];vector <int> v[maxn],p[maxn],G[maxn];
queue <int> Q;
queue <data> Q2;
map <int,int> ma,mb,mc;
map <d2,bool> Edgs;bool cmp(const int &x,const int &y) {return Point[x].x < Point[y].x;}
void Add(int x,int y,int cap)
{v[x].push_back(cnt); edgs[cnt++] = E(y,cap,0);v[y].push_back(cnt); edgs[cnt++] = E(x,0,0);
}
int Tree(const int &x,const int &y,const int &siz)
{return x < y?y:siz - 1 - y;
}int getint()
{char ch = getchar(); int ret = 0,A = 1;while (ch < '0' || '9' < ch){if (ch == '-') A = -1;ch = getchar();}while ('0' <= ch && ch <= '9')ret = ret*10 + ch - '0',ch = getchar();return ret*A;
}void Pre_Work()
{n = getint();for (int i = 1; i <= n; i++){int x = getint(),y = getint();Point[i] = P(x,y); a[i] = y;}Point[++n] = P(0,0); sort(a + 1,a + n + 1);for (int i = 2; i <= n; i++)if (a[i] != a[i-1]) a[++cur] = a[i];for (int i = 1; i <= n; i++){int pos = lower_bound(a + 1,a + cur + 1,Point[i].y) - a;p[pos].push_back(i);}
}void Dp()
{ma[0] = n; mb[0] = n; mc[0] = n; Reach[n][0] = Reach[n][1] = 1;for (int i = 2; i <= cur; i++){int siz = p[i].size(); sort(p[i].begin(),p[i].end(),cmp);for (int j = 0; j < siz; j++){int now = p[i][j]; P o = Point[now]; pos[now] = j;if (ma.count(o.x)){int Pre = ma[o.x]; int w = max(f[Pre],g[Pre]);g[now] = max(g[now],w + 1);G[now].push_back(Pre); Reach[now][1] = 1;}if (mb.count(o.x - o.y)){int Pre = mb[o.x-o.y]; int w = max(f[Pre],g[Pre]);g[now] = max(g[now],w + 1);G[now].push_back(Pre); Reach[now][1] = 1;}if (mc.count(o.x + o.y)){int Pre = mc[o.x+o.y]; int w = max(f[Pre],g[Pre]);g[now] = max(g[now],w + 1);G[now].push_back(Pre); Reach[now][1] = 1;}}int Max = Reach[p[i][0]][1]?g[p[i][0]]:-INF;for (int j = 1; j < siz; j++){int now = p[i][j]; if (Max != -INF) f[now] = Max + 1,Reach[now][0] = 1;if (Reach[now][1])Max = max(Max + 1,g[now] + j);else if (Max != -INF) ++Max;}Max = Reach[p[i][siz-1]][1]?g[p[i][siz-1]]:-INF;for (int j = siz - 2; j >= 0; j--){int now = p[i][j]; if (Max != -INF) f[now] = max(f[now],Max + 1),Reach[now][0] = 1;if (Reach[now][1])Max = max(Max + 1,g[now] + siz - 1 - j);else if (Max != -INF) ++Max;}for (int j = 0; j < siz; j++){int now = p[i][j]; P o = Point[now];if (Reach[now][0] || Reach[now][1])ma[o.x] = mb[o.x-o.y] = mc[o.x+o.y] = now;}}
}void Push_Road(int i,int k,int siz,int Y)
{if (i < pos[k]){for (int j = pos[k] - 1; j > i; j--)Road[++tp] = p[Y][j];for (int j = 0; j <= i; j++)Road[++tp] = p[Y][j];Typ[tp] = 1;}else{for (int j = pos[k] + 1; j < i; j++)Road[++tp] = p[Y][j];for (int j = siz - 1; j >= i; j--)Road[++tp] = p[Y][j];Typ[tp] = 1;}
}void Dfs_Road(int x,int typ)
{Road[++tp] = x;if (typ){for (int i = 0; i < G[x].size(); i++){int to = G[x][i]; bool flag = 0,Nex_typ;if (Reach[to][0] && f[to] + 1 == g[x])flag = 1,Nex_typ = 0;if (Reach[to][1] && g[to] + 1 == g[x])flag = 1,Nex_typ = 1; if (flag){Dfs_Road(to,Nex_typ); return;}}}else{int Y = lower_bound(a + 1,a + cur + 1,Point[x].y) - a,siz = p[Y].size();for (int i = 0; i < siz; i++)if (i != pos[x]){int to = p[Y][i];if (Reach[to][1] && g[to] + Tree(i,pos[x],siz) == f[x]){Push_Road(i,x,siz,Y),--tp;Dfs_Road(Road[tp+1],1); return;}}}
}void Build_Graph()
{for (int i = 1; i <= n; i++){if (f[i] == Ans){vis[i][0] = 1; Q2.push(data(i,0));if (!tp) Dfs_Road(i,0);}if (g[i] == Ans){vis[i][1] = 1; Q2.push(data(i,1));if (!tp) Dfs_Road(i,1);}}while (!Q2.empty()){int k = Q2.front().Num,typ = Q2.front().typ; Q2.pop(); in_road[k] = 1;if (typ){for (int i = 0; i < G[k].size(); i++){int to = G[k][i]; bool flag = 0;if (Reach[to][0] && f[to] + 1 == g[k]){if (!vis[to][0]) vis[to][0] = 1,Q2.push(data(to,0)); flag = 1;}if (Reach[to][1] && g[to] + 1 == g[k]){if (!vis[to][1]) vis[to][1] = 1,Q2.push(data(to,1)); flag = 1; }if (flag && !Edgs.count(d2(to,k))) ++in[k],++out[to],Add(to,k,INF),Edgs[d2(to,k)] = 1;}}else{int Y = lower_bound(a + 1,a + cur + 1,Point[k].y) - a;int siz = p[Y].size();for (int i = 0; i < siz; i++)if (i != pos[k]){int to = p[Y][i];if (Reach[to][1] && g[to] + Tree(i,pos[k],siz) == f[k]){if (!vis[to][1]) vis[to][1] = 1,Q2.push(data(to,1));}}}}for (int i = 1; i <= n; i++)if (in_road[i]){Add(s,i,INF); Add(i,t,INF);int du = in[i] - out[i];if (du > 0) Add(S,i,du),sum += du;else if (du < 0) Add(i,T,-du);}
}int Dfs(int x,int A)
{if (x == T) return A; int flow = 0;for (int &i = Cur[x]; i < v[x].size(); i++){E &e = edgs[v[x][i]];if (e.cap == e.flow || L[e.to] != L[x] + 1) continue;int f = Dfs(e.to,min(A,e.cap - e.flow));if (!f) continue; flow += f; e.flow += f;edgs[v[x][i]^1].flow -= f; A -= f;if (!A) return flow;}if (!flow) L[x] = -1; return flow;
}bool BFS()
{for (int i = 1; i <= T; i++) L[i] = 0;L[S] = 1; Q.push(S);while (!Q.empty()){int k = Q.front(); Q.pop();for (int i = 0; i < v[k].size(); i++){E e = edgs[v[k][i]];if (e.cap == e.flow || L[e.to]) continue;L[e.to] = L[k] + 1; Q.push(e.to);}}return L[T];
}int Dinic()
{int MaxFlow = 0;while (BFS()){for (int i = 1; i <= T; i++) Cur[i] = 0;MaxFlow += Dfs(S,INF);}return MaxFlow;
}int main()
{Pre_Work(); Dp();for (int i = 1; i <= n; i++) Ans = max(Ans,max(f[i],g[i]));cout << Ans << endl; s = n + 1; t = s + 1; S = t + 1; T = S + 1;Build_Graph();for (int i = tp - 1; i > 1; i--) printf("%d ",Road[i]); cout << Road[1] << endl;//if (tp - 1 != Ans) puts("Wrong Answer!"); else puts("Accepted");Dinic(); Add(t,s,INF); cout << Dinic();return 0;
}

4200: [Noi2015]小园丁与老司机相关推荐

  1. P2304 [NOI2015] 小园丁与老司机(网络流/上下界网络流)

    P2304 [NOI2015] 小园丁与老司机 平面上有n个点,每次可以向左.右.上.左上45度.右上45度移动,然后直线移动到达第一个没有到过的点,如果没有这样的点就不能移动,求解一条最长路,然后求 ...

  2. BZOJ4200 洛谷2304 UOJ132:[NOI2015]小园丁与老司机——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...

  3. 提高千倍效率的35个编码小技巧,老司机带你飞!

    点击关注公众号,实用技术文章及时了解 来源:henleylee.github.io/posts/2019/a780fcc1.html 前言 代码优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的 ...

  4. mysql入门到跑路_Mysql入门二十小题(DBA老司机带你删库到跑路)2018.11.26

    1. 请介绍数据库管理系统的种类及代表产品 RDBMS: mysql oracle mssql NoSQL: redis  mongoab  memcache 2. 请简述数据库管理系统的作用 数据存 ...

  5. oracle从删库到跑路,Mysql入门二十小题(DBA老司机带你删库到跑路)2018.11.26

    1. 请介绍数据库管理系统的种类及代表产品 RDBMS: mysql oracle mssql NoSQL: redis  mongoab  memcache 2. 请简述数据库管理系统的作用 数据存 ...

  6. 年薪30W的软件测试“老司机”工作经验

    这几天,新入职的小MM提议"老司机"们把自己这些年的软件测试工作经验跟大家分享一下,让新同学学习学习,利用空闲时间我整理了一些,可能不全,勉强看看,这也算是对自己这些年的工作总结. ...

  7. 【新梦想学员干货】必看!年薪30W的软件测试“老司机”工作经验。

    这几天,新入职的小MM提议"老司机"们把自己这些年的软件测试工作经验跟大家分享一下,让新同学学习学习,利用空闲时间我整理了一些,可能不全,勉强看看,这也算是对自己这些年的工作总结. ...

  8. 名悦集团:开车从不追尾,老司机分享驾驶避免事故小知识

    听交通广播,我们几乎每天都能听到高速路上,高架桥上,上班路上发生追尾事故,有时候是个平常的上下班高峰期.很多人会纳闷,车开的好好的,怎么就会发生追尾事故呢.开车在路上,难免会有磕磕碰碰.道路千万条,安 ...

  9. 老司机都在用的浏览器,体积小功能齐全,直呼内行

    现在市面上的浏览器简直是多不胜数,虽然数量多,但是好用的并不多.尤其是某些大厂的浏览器,无用的功能越来越多,越来越臃肿,体积也越来越大,使用体验还不好,有时候甚至不如一些小众浏览器.今天给大家安利2款 ...

  10. 视频教程-老司机讲前端之微信小程序开发成语消消乐游戏视频课程-微信开发

    老司机讲前端之微信小程序开发成语消消乐游戏视频课程 中国实战派HTML5培训第一人,微软技术讲师,曾任百合网技术总监,博看文思HTML5总监.陶国荣长期致力于HTML5.JavaScript.CSS3 ...

最新文章

  1. Java 最常见的 10000+ 面试题及答案整理:持续更新
  2. Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题
  3. 阿里巴巴 DevOps 工具体系
  4. Unix——《Unix网络编程》配置unp.h头文件
  5. 初识lunece(同义词检索)
  6. websocket实时聊天(一)
  7. 使用给定的整数n,编写一个程序生成一个包含(i, i*i)的字典,该字典包含1到n之间的整数(两者都包含)。假设向程序输入:5则输出为:{1:1, 2:4, 3:9, 4:16, 5:25}
  8. 锐捷服务器虚拟化技术_信息化的好拍档,锐捷助力南昌市第五医院再迎新征程...
  9. 谷歌浏览器翻译插件使用不了,替代品 AnyTranslation
  10. 《Artificial Intelligence in Finance》(AI金融, by Yves Hilpisch)台湾谢承熹Chenghsi Hsieh老师中文讲解
  11. Rpgmakermv(38)MOG_Theatrhythm
  12. 如何做一名优秀的助教@助教的那些事(助教总结)
  13. 渲染优化-从GPU的结构谈起
  14. 茶饮行业舆情管理方案
  15. 3D计算机图形学零起点全攻略
  16. 数据分析-描述数据方法
  17. 算法提高 第五题 java 题解 1096
  18. 计算验证条形码(Ean-13码)
  19. Tizen系统截图曝光 三星I9500或将率先采用
  20. web3j contract 使用方法

热门文章

  1. Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!
  2. 【答读者问16】回测的时候,价格是使用哪一种复权方式(前复权、后复权与不复权)
  3. 卸载手机自带的系统软件
  4. 在MATLAB绘制三角形区域上的二元函数图像
  5. 《八扇屏》贯口全本(共22番)
  6. 分享两个线+标注的SLD样式
  7. [附源码]计算机毕业设计JAVA景区门票系统
  8. iOS 音乐播放器demo讲解
  9. duilib隐藏(显示)任务栏图标方式
  10. PIE Engine机器学习遥感影像监督分类全流程(附源码)