题目:

洛谷 2304

LOJ 2134

(LOJ 上每个测试点有部分分)

写了快一天 …… 好菜啊

分析:

毒瘤二合一题 ……

注意本题(及本文)使用 \(x\) 向右,\(y\) 向上的「数学坐标系」,而不是 \(x\) 向下,\(y\) 向右的所谓「OI 坐标系」。「同一行」指 \(y\) 相同,「同一列」指 \(x\) 相同。

老司机

注意,只能在 没有经过的 树下转向,并且每棵树只能访问一次。

第一反应是 \(f_{u}\) 表示从点 \(u\) (树 \(u\) )出发能走到的最多的点数(不含本身),记忆化搜索,则答案就是 \(f_0\) (令 \((0,0)\) 为 \(0\) 号点)。然而,这样转移是有环的,因为同一行中互相可达。

那么就考虑逐行转移。先对于所有点 \(u\) 处理出所有不同行的可达的 \(v\) (即 \(u\) 与 \(v\) 的横坐标相等、横纵坐标之和相等或横纵坐标之差相等且 \(u\) 与 \(v\) 的连线上没有其他点)。计算 \(f(u)\) 时,先枚举它在同行走到了哪个点,再枚举它从那个点走到了上方的哪个点。设当前点是该行的第 \(p\) 个点,要走到同行的第 \(k\) 个点。若 \(p<k\) ,则最优策略是先走完左侧的所有点,再向右走到 \(k\) ,然后向上走;若 \(p=k\) ,则只能直接向上走;若 \(p>k\) ,则先走完右侧的所有点,再向左走到 \(k\) 。形式化地(其中 \(p_{i,j}\) 表示 \(y=i\) 的横坐标从小到大第 \(j\) 个点,标号从 \(0\) 开始。\(a_i\) 表示 \(y=i\) 的总点数):

\[f_{p_{y,i}}=\max\begin{cases}
a_y-k-1\ (k<i且p_{y,k}无出边)\\
f_v+a_y-k\ (k<i且p_{y,k}到v有连边)\\
f_v+1\ (p_{y,i}到v有连边)\\
k\ (k>i且p_{y,k}无出边)\\
f_v+k+1\ (k>i且p_{y,i}到v有连边)\\
\end{cases}
\]

(注意要讨论边界情况。状态定义中不含 \(p_{y,i}\) 这点本身的贡献)

时间复杂度 \(O(nm)\) ,其中 \(m\) 表示每行最大的点数。题面上说 \(m\leq 1000\) 但是小恐龙说数据里有到 \(1600\) 多的?

至于输出方案,转移的时候记录一下转移时用的 \(k\) 是哪个,然后在 \(p_{y,k}\) 的出边中找哪个点的 \(f\) 值加上 \(a_y-k-1\) 之类的东西(视 \(k\) 和 \(i\) 的大小关系而定)等于当前点的 \(f\) 值,走过去就 OK 啦。

于是第一问就 愉快地 解决啦。

小园丁

首先用和输出方案类似的方法处理出哪些连边是可能存在于最优路径中的。然后考虑限制:

压路机可以从任意一棵树下出发,在任意一棵树下停止

压路机只能经过可能存在于最优路径中的边,每条边要被经过至少一次\

这不是 …… 网络流??

每条边的权值是 \([1,+\infty)\) ,然后源点向所有点连 \([0,+\infty)\) ,所有点向汇点连 \([0,+\infty)\) ,然后就是一个上下界有源汇最小流板子了。(啥玩意?) 我是照着小恐龙的博客现学的。

为酒肉朋友打广告:有上下界的网络流/费用流 学习笔记 - Little Dino

代码:

写自闭了 ……

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
#include <map>
#include <queue>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef pair<int, int> pii;
const int N = 5e4 + 10, M = 1e3 + 10, INF = 0x3f3f3f3f;
int n, X[N], Y[N], head[N], ecnt, ycnt;
pii pos[N], dp[N];
bool vis[N];
vector<int> id[N];
map<int, vector<int> > mp;
struct edge
{
int to, next;
}e[N * 3];
void add(const int a, const int b)
{
e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
}
bool cmpx(const int a, const int b)
{
return X[a] < X[b];
}
void init()
{
static map<int, int> x, x_add_y, x_sub_y;
memset(head, -1, sizeof(int[n + 1]));
for (int i = 1; i <= n; i++)
mp[Y[i]].push_back(i);
for (auto it = mp.rbegin(); it != mp.rend(); it++)
{
sort(it->second.begin(), it->second.end(), cmpx);
id[++ycnt] = it->second;
for (auto itt = it->second.begin(); itt != it->second.end(); itt++)
{
int u = *itt;
pos[u] = pii(ycnt, itt - it->second.begin());
if (x.count(X[u]))
add(u, x[X[u]]);
x[X[u]] = u;
if (x_add_y.count(X[u] + Y[u]))
add(u, x_add_y[X[u] + Y[u]]);
x_add_y[X[u] + Y[u]] = u;
if (x_sub_y.count(X[u] - Y[u]))
add(u, x_sub_y[X[u] - Y[u]]);
x_sub_y[X[u] - Y[u]] = u;
}
}
}
template<typename T>
inline void get_max(T &a, const T b)
{
a = max(a, b);
}
int dfs(const int u)
{
int y = pos[u].first, p = pos[u].second;
if (vis[u])
return dp[u].first;
vis[u] = true;
dp[u] = pii(0, 0);
for (int k = 0; k < p; k++)
{
int now = id[y][k], res = 0;
for (int i = head[now]; ~i; i = e[i].next)
{
int v = e[i].to;
get_max(res, dfs(v));
}
get_max(dp[u], pii(res + id[y].size() - k - (head[now] == -1), now));
}
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
get_max(dp[u], pii(dfs(v) + 1, u));
}
for (int k = p + 1; k < id[y].size(); k++)
{
int now = id[y][k], res = 0;
for (int i = head[now]; ~i; i = e[i].next)
{
int v = e[i].to;
get_max(res, dfs(v));
}
get_max(dp[u], pii(res + k + (head[now] != -1), now));
}
return dp[u].first;
}
void work1()
{
int ans = dfs(n);
write(ans), putchar('\n');
int tmp = n;
while (tmp)
{
if (tmp != n)
write(tmp), putchar(' ');
int y = pos[tmp].first, p = pos[tmp].second, nxt = dp[tmp].second, delta = 1, nxtt = 0;
if (!nxt)
break;
if (pos[nxt].second < p)
{
for (int i = p + 1; i < id[y].size(); i++)
write(id[y][i]), putchar(' '), ++delta;
for (int i = p - 1; i >= pos[nxt].second; i--)
write(id[y][i]), putchar(' '), ++delta;
}
else if (pos[nxt].second > p)
{
for (int i = p - 1; i >= 0; i--)
write(id[y][i]), putchar(' '), ++delta;
for (int i = p + 1; i <= pos[nxt].second; i++)
write(id[y][i]), putchar(' '), ++delta;
}
for (int i = head[nxt]; ~i; i = e[i].next)
{
int v = e[i].to;
if (dp[v].first + delta == dp[tmp].first)
{
nxtt = v;
break;
}
}
tmp = nxtt;
}
putchar('\n');
}
namespace Subtask2
{
int s, t, S, T, out[N];
namespace Dinic
{
struct edge
{
int to, w, next;
}e[N * 12];
int cur[N], dis[N], head[N], ecnt, s, t;
void add(const int a, const int b, const int c)
{
e[ecnt] = (edge){b, c, head[a]}, head[a] = ecnt++;
}
void addtw(const int a, const int b, const int c)
{
add(a, b, c), add(b, a, 0);
}
bool bfs()
{
memset(dis, -1, sizeof(int[T + 1]));
memcpy(cur, head, sizeof(int[T + 1]));
static queue<int> q;
q.push(s);
dis[s] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if (e[i].w && dis[v] == -1)
dis[v] = dis[u] + 1, q.push(v);
}
}
return dis[t] != -1;
}
int dfs(const int u, const int minn)
{
if (u == t || !minn)
return minn;
int used = 0;
for (int &i = cur[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if (e[i].w && dis[v] == dis[u] + 1)
{
int w = dfs(v, min(minn - used, e[i].w));
e[i].w -= w, e[i ^ 1].w += w, used += w;
if (used == minn)
break;
}
}
return used;
}
int work(const int _s, const int _t)
{
s = _s, t = _t;
int ans = 0;
while (bfs())
ans += dfs(s, INF);
return ans;
}
}
void work()
{
using Dinic::addtw;
static queue<int> q;
static bool vis[N], mark[N * 3];
s = n + 1, t = n + 2, S = n + 3, T = n + 4;
memset(Dinic::head, -1, sizeof(int[T + 1]));
for (int i = 1; i <= n; i++)
addtw(s, i, INF), addtw(i, t, INF);
q.push(n);
while (!q.empty())
{
int u = q.front();
int y = pos[u].first, p = pos[u].second;
q.pop();
for (int k = 0; k < p; k++)
{
int now = id[y][k], delta = id[y].size() - k;
for (int i = head[now]; ~i; i = e[i].next)
{
int v = e[i].to;
if (!mark[i] && dp[v].first + delta == dp[u].first)
{
++out[now], --out[v], addtw(now, v, INF), mark[i] = true;
if (!vis[v])
q.push(v), vis[v] = true;
}
}
}
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if (!mark[i] && dp[v].first + 1 == dp[u].first)
{
++out[u], --out[v], addtw(u, v, INF), mark[i] = true;
if (!vis[v])
q.push(v), vis[v] = true;
}
}
for (int k = p + 1; k < id[y].size(); k++)
{
int now = id[y][k], delta = k + 1;
for (int i = head[now]; ~i; i = e[i].next)
{
int v = e[i].to;
if (!mark[i] && dp[v].first + delta == dp[u].first)
{
++out[now], --out[v], addtw(now, v, INF), mark[i] = true;
if (!vis[v])
q.push(v), vis[v] = true;
}
}
}
}
for (int i = 1; i <= n; i++)
if (out[i] < 0)
addtw(S, i, -out[i]);
else
addtw(i, T, out[i]);
addtw(t, s, INF);
Dinic::work(S, T);
int flow = Dinic::e[Dinic::ecnt - 1].w;
Dinic::e[Dinic::ecnt - 1].w = Dinic::e[Dinic::ecnt - 2].w = 0;
write(flow - Dinic::work(t, s));
}
}
int work()
{
read(n);
for (int i = 1; i <= n; i++)
read(X[i]), read(Y[i]);
++n, X[n] = Y[n] = 0;
init();
work1();
Subtask2::work();
return 0;
}
}
int main()
{
#ifdef BlueSpirit
freopen("2304.in", "r", stdin);
#endif
return zyt::work();
}

【洛谷2304_LOJ2134】[NOI2015]小园丁与老司机(动态规划_网络流)的更多相关文章

  1. uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】

    题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...

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

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

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

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

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

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

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

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

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

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

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

    Time Limit: 20 Sec  Memory Limit: 512 MBSec  Special Judge Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维 ...

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

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

  9. 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流

    题目描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤ ...

随机推荐

  1. Exchanger使用

    Exchanger使用

  2. JSX 语法

    jsx 不能直接运行,是被 babel-loader 中的 react 这个 preset 翻译的 需要注意: 1.必须被一个单独的大标签包裹,比如:div 或者 section 2.标签必须封闭 3 ...

  3. Jenkins系列之-—04 配置用户和权限控制

    一.安装插件 插件名称:Role-based Authorization Strategy Role Strategy Plugin插件可以对构建的项目进行授权管理,让不同的用户管理不同的项目. 二. ...

  4. Selenium系列之--04 常见元素操作总结

    一.Selenium总共有八种定位方法  By.id()  通过id定位 By.name()  通过name 定位 By.xpath() 通过xpath定位 By.className() 通过clas ...

  5. Python爬虫开发【第1篇】【机器视觉及Tesseract】

    ORC库概述 在读取和处理图像.图像相关的机器学习以及创建图像等任务中,Python 一直都是非常出色的语言.虽然有很多库可以进行图像处理,但在这里我们只重点介绍:Tesseract 1.Tesser ...

  6. EJB学习笔记六(EJB中的拦截器)

     1.前言 听到拦截器,预计都不陌生,尤其是在Servlet规范中,充分应用了拦截器的概念.EJB3也提供了拦截器的支持,本质上是轻量级的AOP实现.拦截器能够将多个业务方法中的通用逻辑从业务方法中抽 ...

  7. leetCode(26):Unique Binary Search Trees

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  8. LeetCode 1.两数之和(JS)

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  9. mysql08---优化01

    Mysql数据库的优化技术 对mysql优化时一个综合性的技术,主要包括 a: 表的设计合理化(符合3NF) b: 添加适当索引(index) [四种: 普通索引(什么都不写).主键索引(有一个主键 ...

  10. I.MX6 各模块 clock 查询

    /********************************************************************* * I.MX6 各模块 clock 查询 * 说明: * ...