题目链接

uoj132

题解

真是一道大码题,,,肝了一个上午

老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\)是每层的点数

我们设\(f[i]\)表示从\(i\)点进入该层,直到走完为止所经过的最多点的数量,我们把原点也看做一棵树,计算答案时减去即可

转移只需枚举出点\(j\),假如\(i\)在\(j\)的左侧,那么\(j\)及其左侧的点都能被经过,只需从\(i\)出发先走到左端点,再一直往右走到\(j\)

我们还需预处理出每个点向三个方向能到达的下一个点,这个使用排序 + 离散化 + 桶即可

具体地,向上记录\(y\),向左上记录\(x + y\),向右上记录\(y - x\)

原理是利用一次方程\(y = x + b\)和\(y = -x + b\)

好了\(dp\)的部分解决了,稍微记录一下转移就可以输出方案

现在我们解决小园丁的部分

显而易见这是一个带上下界网络流

对于可能出现的每条路径,都是一条流量下界为\(1\)的边

建源汇点\(S\)和\(T\)连向所有点

然后建超级源汇点跑一遍最小可行流即可

直接跑比较慢,需要用满流减去不加\(T->S\)的流量最大流

口胡真容易

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (register int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (register int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 100005,maxm = 1000005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int n,x[maxn],y[maxn],val[maxn],val2[maxn],b[maxn],bi,tot;
int nxtl[maxn],nxt[maxn],nxtr[maxn];
int bac[maxn],id[maxn],sit[maxn];
int L[maxn],R[maxn],pos[maxn],cnt;
int f[maxn],Nxt[maxn],Out[maxn];
int ok[maxn],d[maxn],vis[maxn],used[maxn],q[maxn],cur[maxn],head,tail,now;
int df[maxn],h[maxn],ne = 1;
struct EDGE{int to,nxt,f;}ed[maxm];
inline void build(int u,int v,int w){
ed[++ne] = (EDGE){v,h[u],w}; h[u] = ne;
ed[++ne] = (EDGE){u,h[v],0}; h[v] = ne;
}
void add(int u,int v){
Redge(u) if ((to = ed[k].to) == v) return;
ed[++ne] = (EDGE){v,h[u],INF}; h[u] = ne;
ed[++ne] = (EDGE){u,h[v],0}; h[v] = ne;
df[u]--; df[v]++; ok[v] = true;
}
inline bool cmp(const int& a,const int& b){
return val[a] == val[b] ? val2[a] < val2[b] : val[a] < val[b];
}
void preset(){
REP(i,n) id[i] = i,val[i] = y[i],b[++bi] = x[i];
sort(id + 1,id + 1 + n,cmp);
sort(b + 1,b + 1 + bi); tot = 1;
for (int i = 2; i <= bi; i++) if (b[i] != b[tot]) b[++tot] = b[i];
for (int i = n; i; i--){
int u = id[i],X = lower_bound(b + 1,b + 1 + tot,x[u]) - b;
nxt[u] = bac[X];
bac[X] = u;
}
bi = 0; cls(bac);
REP(i,n) val[i] = x[i] + y[i],val2[i] = x[i],b[++bi] = x[i] + y[i];
sort(id + 1,id + 1 + n,cmp);
sort(b + 1,b + 1 + bi); tot = 1;
for (int i = 2; i <= bi; i++) if (b[i] != b[tot]) b[++tot] = b[i];
for (int i = 1; i <= n; i++){
int u = id[i],V = lower_bound(b + 1,b + 1 + tot,x[u] + y[u]) - b;
nxtl[u] = bac[V];
bac[V] = u;
}
bi = 0; cls(bac);
REP(i,n) val[i] = y[i] - x[i],val2[i] = x[i],b[++bi] = y[i] - x[i];
sort(id + 1,id + 1 + n,cmp);
sort(b + 1,b + 1 + bi); tot = 1;
for (int i = 2; i <= bi; i++) if (b[i] != b[tot]) b[++tot] = b[i];
for (int i = n; i; i--){
int u = id[i],V = lower_bound(b + 1,b + 1 + tot,y[u] - x[u]) - b;
nxtr[u] = bac[V];
bac[V] = u;
}
REP(i,n) val[i] = y[i];
sort(id + 1,id + 1 + n,cmp);
for (int i = 1; i <= n; i++){
int u = id[i]; sit[u] = i;
if (i == 1 || y[u] != y[id[i - 1]]){
L[++cnt] = i;
if (cnt > 1) R[cnt - 1] = i - 1;
}
pos[i] = cnt;
}
R[cnt] = n;
}
void work_dp(){
for (int i = L[cnt]; i <= n; i++) f[id[i]] = n - L[cnt] + 1;
for (int i = cnt - 1; i; i--){
int mx = 0,now,nowf,tmp,to,from; now = 0; from = 0;
for (int j = L[i]; j <= R[i]; j++){
int u = id[j];
tmp = 0; to = 0; from = 0;
if (nxt[u] && f[nxt[u]] > tmp) tmp = f[nxt[u]],to = nxt[u],from = u;
if (nxtl[u] && f[nxtl[u]] > tmp) tmp = f[nxtl[u]],to = nxtl[u],from = u;
if (nxtr[u] && f[nxtr[u]] > tmp) tmp = f[nxtr[u]],to = nxtr[u],from = u;
tmp++;
if (tmp > f[u]) f[u] = tmp,Nxt[u] = to,Out[u] = from;
tmp += R[i] - j;
if (mx > f[u]) f[u] = mx,Nxt[u] = now,Out[u] = nowf;
if (tmp > mx) mx = tmp,now = to,nowf = u;
}
mx = 0; now = 0; from = 0;
for (int j = R[i]; j >= L[i]; j--){
int u = id[j];
tmp = 0; to = 0; from = 0;
if (nxt[u] && f[nxt[u]] > tmp) tmp = f[nxt[u]],to = nxt[u],from = u;
if (nxtl[u] && f[nxtl[u]] > tmp) tmp = f[nxtl[u]],to = nxtl[u],from = u;
if (nxtr[u] && f[nxtr[u]] > tmp) tmp = f[nxtr[u]],to = nxtr[u],from = u;
tmp++;
if (tmp > f[u]) f[u] = tmp,Nxt[u] = to,Out[u] = nowf;
tmp += j - L[i];
if (mx > f[u]) f[u] = mx,Nxt[u] = now,Out[u] = nowf;
if (tmp > mx) mx = tmp,now = to,nowf = u;
}
}
}
void print(){
printf("%d\n",f[n] - 1);
int u = Nxt[n],now,num = f[n] - 1;
while (u){
now = pos[sit[u]];
if (now == cnt){
printf("%d ",u),num--; if (!num) break;
for (int i = sit[u] - 1; num && i >= L[cnt]; i--){
printf("%d ",id[i]),num--; if (!num) break;
}
for (int i = sit[u] + 1; num && i <= R[cnt]; i++){
printf("%d ",id[i]),num--; if (!num) break;
}
break;
}
if (x[Out[u]] == x[u]){
printf("%d ",u),num--;
}
else if (x[Out[u]] <= x[u]){
for (int i = sit[u]; num && i <= R[now]; i++){
printf("%d ",id[i]),num--; if (!num) break;
}
for (int i = sit[u] - 1; num && i >= sit[Out[u]]; i--){
printf("%d ",id[i]),num--; if (!num) break;
}
}
else {
for (int i = sit[u]; num && i >= L[now]; i--){
printf("%d ",id[i]),num--; if (!num) break;
}
for (int i = sit[u] + 1; num && i <= sit[Out[u]]; i++){
printf("%d ",id[i]),num--; if (!num) break;
}
}
if (!num) break;
u = Nxt[u];
}
puts("");
}
void solve1(){
preset();
work_dp();
print();
}
bool bfs(int S,int T){
q[head = tail = 0] = S; now++;
int u;
while (head <= tail){
u = q[head++];
Redge(u) if (ed[k].f && vis[to = ed[k].to] != now){
d[to] = d[u] + 1;
vis[to] = now;
q[++tail] = to;
if (to == T) return true;
}
}
return false;
}
int dfs(int u,int minf,int T){
if (u == T || !minf) return minf;
int f,flow = 0,to;
if (used[u] != now) used[u] = now,cur[u] = h[u];
for (int& k = cur[u]; k; k = ed[k].nxt)
if (vis[to = ed[k].to] == now && d[to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f),T))){
ed[k].f -= f; ed[k ^ 1].f += f;
flow += f; minf -= f;
if (!minf) break;
}
return flow;
}
void work_sol(int u){
int S = pos[sit[u]],l = L[S],r = R[S],a,b,c,v,sum;
for (int i = l; i <= r; i++){
v = id[i]; a = nxtl[v]; b = nxt[v]; c = nxtr[v];
if (v == u){
if (a && f[a] + 1 == f[u]) add(v,a);
if (b && f[b] + 1 == f[u]) add(v,b);
if (c && f[c] + 1 == f[u]) add(v,c);
continue;
}
if (sit[v] < sit[u]) sum = r - sit[v] + 1;
else sum = sit[v] - l + 1;
if (a && f[a] + sum == f[u]) add(v,a);
if (b && f[b] + sum == f[u]) add(v,b);
if (c && f[c] + sum == f[u]) add(v,c);
}
}
void setG(){
ok[n] = true;
for (int i = 1; i < cnt; i++){
for (int j = L[i]; j <= R[i]; j++)
if (ok[id[j]]) work_sol(id[j]);
}
}
void solve2(){
setG();
int S = n + 1,T = n + 2,ans = 0;
for (int i = 1; i <= n; i++){
if (!df[i]) continue;
if (df[i] > 0) ans += df[i],build(S,i,df[i]);
else build(i,T,-df[i]);
}
while (bfs(S,T)) ans -= dfs(S,INF,T);
printf("%d\n",ans);
}
int main(){
n = read();
REP(i,n) x[i] = read(),y[i] = read();
n++;
solve1();
solve2();
return 0;
}

uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】的更多相关文章

  1. BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)

    一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...

  2. luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流

    LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...

  3. 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流

    [BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...

  4. 并不对劲的loj2134:uoj132:p2304:[NOI2015]小园丁与老司机

    题目大意 给出平面直角坐标系中\(n\)(\(n\leq5*10^4\))个点,第\(i\)个点的坐标是\(x_i,y_i(|x_i|\leq10^9,1\leq y_i\leq10^9)\),只有朝 ...

  5. [NOI2015]小园丁与老司机(DP+上下界最小流)

    由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法. 先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现. 第一问:以行为阶段,对于每行,暴力枚举最有路径 ...

  6. [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机

    [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...

  7. [BZOJ4200][Noi2015]小园丁与老司机

    4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 106  Solved ...

  8. [Noi2015]小园丁和老司机

    来自FallDream的博客,未经允许,请勿转载,谢谢. 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有n棵许愿树,编号1,2,3,…,n,每棵树可以看作平面上的一个点,其中 ...

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

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

随机推荐

  1. VS2017+CMake+OpenCV下报错 set OpenCV_FOUND to FALSE

    问题 在 VS 2017 中使用Cmake 管理项目, 使用 opencv 库, 在find package的时候出现能找到 OpenCVConfig.cmake的文件,但是设置 OpenCV_Fou ...

  2. zabbix切换中文,监控图下方显示乱码,监控图X轴不显示时间问题解决(适用于所有版本)

    一.现象: abbix3.4安装好后添加zabbix图形,发现有好多方块 这是因为zabbix web程序缺少中文字体 二.解决方案1: 1.在windows系统找一个中文字体上传到服务器中,我这里找 ...

  3. Bootstrap学习--基本格式

    以下为Bootstrap的基本格式代码 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta ...

  4. scrapy笔记集合

    细读http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html 目录 Scrapy介绍 安装 基本命令 项目结构以及爬虫应用介绍 简单使用示例 选 ...

  5. (第十周)评论Beta发布

    本人所在组:奋斗吧兄弟 按课上展示的顺序对每组进行点评: 1.  飞天小女警 项目:礼物挑选工具 相对于alpha发布时有了很大的进步.项目的界面很漂亮,这个项目的想法很新颖,我很喜欢.礼物的挑选给出 ...

  6. Task 6.2冲刺会议四 /2015-5-17

    今天主要是学习并熟悉了C#的开发流程,把他的文件的大体结构和每个组件之间的联系弄清楚之后.开始写服务器部分的内容.学习过程中,感觉网上的资料有些太鱼龙混杂了,不知道该怎么取舍.明天准备完善服务器的功能 ...

  7. 《Spring2之站立会议8》

    <Spring2之站立会议8> 昨天,添加了登录界面: 今天,准备添加注册界面: 遇到的问题:过程中遇到了一些困难,不过还是解决了.

  8. 给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。

    一.题目: n给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数. n要求: n写一个函数 f(N) ,返回1 到 N 之间出现的 “1”的个数.例如 f(12)  ...

  9. Python对list去重

    Python对list去重 方法一 新建新的列表,利用not in命令去重.这种方法看起来不够简便,但是保留了原列表中的顺序.代码如下: list1 = [1,2,3,4,1,1,2,5,4,3] l ...

  10. 【转】python 三种遍历list的方法

    [转]python 三种遍历list的方法 #!/usr/bin/env python # -*- coding: utf-8 -*- if __name__ == '__main__': list ...