「NOI2015」小园丁与老司机
「NOI2015」小园丁与老司机
要不是这道码农题,去年就补完了NOI2015,其实两问都比较simple,但是写起来很恶心。
先解决第一问,记 \(dp[i]\) 表示老司机到达第 \(i\) 棵树时能许愿的最多的树的数量,考虑没有左右方向的决策时,转移是个DAG,直接做。 对于左右方向的决策,会发现最优解一定是以下两种中的一种。
- 对于点 \(x\) ,从左边的某个点 \(y\) 进入这一行,然后先向左走走到最左边的一棵树,然后再调头走到点 \(x\) 。
- 对于点 \(x\) ,从右边的某个点 \(y\) 进入这一行,然后先向右走走到最右边的一棵树,然后再调头走到点 \(x\)。
那么就将 \(y\) 坐标相同的点放在一起转移,维护一下前缀和后缀 \(\max\) 即可。
第一个比较恶心的点在于输出方案,单纯记录每个点是由谁转移的话会发现有环,需要记录一下每个点由左右转移过来的方案和由其它转移过来的方案,当发现某一步是由左右转移过来的时候,下一步钦点不能由左右转移过来即可。
考虑第二问,没有了左右的转移边,相当于将所有其它的且在某个最优解上的转移构成的DAG取出来,求这个DAG的最小链覆盖,直接跑有源汇上下界最小流即可。
这里有第二个比较恶心的点,求一个转移是否在最优解上非常麻烦,需要求一个 \(dp'[i]\) 表示老司机从 \(i\) 出发能许愿的最多的树的数量,还是左右方向的决策,这次变成了
对于点 \(x\) ,先往左走一直走到底,再往右走走到其右边的某一棵树 \(y\) 往上走。
对于点 \(x\) ,先往右走一周走到底,再往左走走到其左边的某一棵树 \(y\) 往上走。
把式子拆开来发现还是可以前缀和以及后缀和维护的,这个地方比较容易晕,想我这种比较菜的人就要多想想。
code
/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define fi first
#define se second
const int N = 1000005, M = 2000005;
vector<int> ed[N], fx[N];
vector<pair<int, int> > v1[N], v2[N], v3[N], v4[N];
int s[N], ax[N], ay[N], fuck[N], n, len;
inline int D(int x){ return lower_bound(s + 1, s + len + 1, x) - s; }
namespace flow{
queue<int> q;
int a[M], cap[M], nxt[M], head[N], cur[N], dis[N], S, T, cnt = 1;
inline void addedge(int x, int y, int z){
a[++cnt] = y, cap[cnt] = z, nxt[cnt] = head[x], head[x] = cnt;
a[++cnt] = x, cap[cnt] = 0, nxt[cnt] = head[y], head[y] = cnt;
}
inline int bfs(){
memset(dis, -1, sizeof(dis)), dis[S] = 0, q.push(S);
for(; !q.empty(); q.pop()){
int u = q.front();
for(int p = head[u]; p; p = nxt[p]){
int v = a[p];
if(~dis[v] || !cap[p]) continue;
dis[v] = dis[u] + 1, q.push(v);
}
}
return ~dis[T];
}
inline int dfs(int u, int flow){
if(u == T || !flow) return flow;
int used = 0;
for(int &p = cur[u]; p; p = nxt[p]){
int v = a[p];
if(dis[v] != dis[u] + 1 || !cap[p]) continue;
int x = dfs(v, min(flow, cap[p]));
used += x, flow -= x, cap[p] -= x, cap[p^1] += x;
if(!flow) break;
}
return used;
}
inline void setflow(int x, int y){ S = x, T = y; }
inline int getflow(){
int res = 0;
for(; bfs(); res += dfs(S, inf))
for(int i = 1; i <= n + 4; i++) cur[i] = head[i];
return res;
}
}
namespace task2{
int NS, NT, S, T;
inline void init(){
NS = n + 1, NT = n + 2, S = n + 3, T = n + 4;
}
inline void add(int x, int y, int a, int b){
flow::addedge(NS, y, a);
flow::addedge(x, NT, a);
flow::addedge(x, y, b - a);
}
inline void solve(){
for(int i = 1; i <= n; i++){
add(S, i, 0, inf);
add(i, T, 0, inf);
}
flow::setflow(NS, NT);
flow::getflow();
flow::addedge(T, S, inf);
cout << flow::getflow() << endl;
}
}
namespace task1{
int f[N], ff[N], st[N], g[N], id[N], gg[N], pr[N], spec[N], ans, top;
inline void chkmax(int &x, int y){ if(y > x) x = y; }
inline bool cmp(int x, int y){
return ay[x] == ay[y] ? ax[x] > ax[y] : ay[x] < ay[y];
}
inline void trans(int x){
for(int i = 0; i < (int) ed[x].size(); i++)
if(!spec[ed[x][i]] && f[x] + 1 > f[ed[x][i]])
f[ed[x][i]] = f[x] + 1, pr[ed[x][i]] = x;
}
inline void trans2(int x){
for(int i = 0; i < (int) fx[x].size(); i++)
if(!spec[fx[x][i]]) chkmax(ff[fx[x][i]], ff[x] + 1);
}
inline void solve(){
for(int i = 1; i <= n; i++) id[i] = i;
for(int i = 1; i <= n; i++) f[i] = g[i] = -inf, ff[i] = 1;
f[n] = ff[n] = 0;
sort(id + 1, id + n + 1, cmp);
for(int i = 1, last; i <= n; i = last + 1){
last = i;
while(last < n && ay[id[last+1]] == ay[id[i]]) last++;
for(int j = i; j <= last; j++) spec[id[j]] = 1;
int Left = -inf, Right = -inf, posL = 0, posR = 0;
for(int j = i; j <= last; j++){
if(Left != -inf) g[id[j]] = Left + j - i, gg[id[j]] = posL;
if(f[id[j]] > Left) Left = f[id[j]], posL = id[j];
}
for(int j = last; j >= i; j--){
if(Right != -inf && Right + last - j > g[id[j]])
g[id[j]] = Right + last - j, gg[id[j]] = posR;
if(f[id[j]] > Right) Right = f[id[j]], posR = id[j];
}
for(int j = i; j <= last; j++){
if(g[id[j]] > f[id[j]]) f[id[j]] = g[id[j]], fuck[id[j]] = 1;
if(f[id[j]] != -inf) trans(id[j]);
}
for(int j = i; j <= last; j++) spec[id[j]] = 0;
}
for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
int pos = n;
for(int i = 1; i <= n; i++) if(f[i] == ans) pos = i;
while(pos != n){
if(fuck[pos]){
int l = lower_bound(v1[D(ay[pos])].begin(), v1[D(ay[pos])].end(), make_pair(ax[pos], pos)) - v1[D(ay[pos])].begin();
int r = lower_bound(v1[D(ay[pos])].begin(), v1[D(ay[pos])].end(), make_pair(ax[gg[pos]], gg[pos])) - v1[D(ay[pos])].begin();
if(l < r){
for(int i = l; i < r; i++) st[++top] = v1[D(ay[pos])][i].se;
for(int i = (int) v1[D(ay[pos])].size() - 1; i > r; i--) st[++top] = v1[D(ay[pos])][i].se;
}
else{
for(int i = l; i > r; i--) st[++top] = v1[D(ay[pos])][i].se;
for(int i = 0; i < r; i++) st[++top] = v1[D(ay[pos])][i].se;
}
pos = gg[pos], fuck[pos] = 0;
}
else st[++top] = pos, pos = pr[pos];
}
printf("%d\n", ans);
for(int i = top; i >= 1; i--) printf("%d ", st[i]);
puts("");
reverse(id + 1, id + n + 1);
for(int i = 1; i <= n; i++) g[i] = -inf;
for(int i = 1, last; i <= n; i = last + 1){
last = i;
while(last < n && ay[id[last+1]] == ay[id[i]]) last++;
for(int j = i; j <= last; j++) spec[id[j]] = 1;
int Left = -inf, Right = -inf;
for(int j = i; j <= last; j++){
g[id[j]] = Left + last;
chkmax(Left, ff[id[j]] - j);
}
for(int j = last; j >= i; j--){
g[id[j]] = Max(g[id[j]], Right - i);
chkmax(Right, ff[id[j]] + j);
}
for(int j = i; j <= last; j++){
ff[id[j]] = Max(ff[id[j]], g[id[j]]);
if(ff[id[j]] != -inf) trans2(id[j]);
}
for(int j = i; j <= last; j++) spec[id[j]] = 0;
}
for(int i = 1; i <= n; i++)
for(int j = 0; j < (int) ed[i].size(); j++){
int v = ed[i][j];
if(ay[v] == ay[i] || v == n) continue;
if(f[i] + ff[v] == ans)
task2::add(i, v, 1, inf);
}
}
}
inline void linkpre(vector<pair<int, int> > &A, pair<int, int> x){
vector<pair<int, int> >::iterator it;
it = lower_bound(A.begin(), A.end(), x);
if(it != A.begin()) --it, ed[x.se].push_back(it->se);
}
inline void linknxt(vector<pair<int, int> > &A, pair<int, int> x){
vector<pair<int, int> >::iterator it;
it = upper_bound(A.begin(), A.end(), x);
if(it != A.end()) ed[x.se].push_back(it->se);
}
int main(){
read(n), s[++len] = 0, ++n;
for(int i = 1; i < n; i++){
read(ax[i]), read(ay[i]);
s[++len] = ax[i], s[++len] = ay[i];
s[++len] = ax[i] + ay[i], s[++len] = ax[i] - ay[i];
}
sort(s + 1, s + len + 1);
len = unique(s + 1, s + len + 1) - s - 1;
for(int i = 1; i <= n; i++){
v1[D(ay[i])].push_back(make_pair(ax[i], i));
v2[D(ax[i])].push_back(make_pair(ay[i], i));
v3[D(ax[i]-ay[i])].push_back(make_pair(ay[i], i));
v4[D(ax[i]+ay[i])].push_back(make_pair(ay[i], i));
}
for(int i = 1; i <= len; i++){
sort(v1[i].begin(), v1[i].end());
sort(v2[i].begin(), v2[i].end());
sort(v3[i].begin(), v3[i].end());
sort(v4[i].begin(), v4[i].end());
}
for(int i = 1; i <= n; i++){
linkpre(v1[D(ay[i])], make_pair(ax[i], i));
linknxt(v1[D(ay[i])], make_pair(ax[i], i));
linknxt(v2[D(ax[i])], make_pair(ay[i], i));
linknxt(v3[D(ax[i]-ay[i])], make_pair(ay[i], i));
linknxt(v4[D(ax[i]+ay[i])], make_pair(ay[i], i));
}
for(int i = 1; i <= n; i++)
for(int j = 0; j < (int) ed[i].size(); j++)
fx[ed[i][j]].push_back(i);
task2::init();
task1::solve();
task2::solve();
return 0;
}
「NOI2015」小园丁与老司机的更多相关文章
- *LOJ#2134. 「NOI2015」小园丁与老司机
$n \leq 5e4$个平面上的点,从原点出发,能从当前点向左.右.上.左上或右上到达该方向最近的给定点.问三个问:一.最多经过多少点:二.前一问的方案:三.其所有方案种非左右走的边至少要开几辆挖掘 ...
- 【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
[BZOJ4200][NOI2015]小园丁与老司机(动态规划,网络流) 题面 BZOJ权限题,洛谷链接 题解 一道二合一的题目 考虑第一问. 先考虑如何计算六个方向上的第一个点. 左右上很好考虑,只 ...
- UOJ132 【NOI2015】小园丁与老司机
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- [BZOJ4200][Noi2015]小园丁与老司机
4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 106 Solved ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
[BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...
- luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...
- [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机 题意 给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题. 第一个问题是从原 ...
- 【洛谷2304_LOJ2134】[NOI2015]小园丁与老司机(动态规划_网络流)
题目: 洛谷 2304 LOJ 2134 (LOJ 上每个测试点有部分分) 写了快一天 -- 好菜啊 分析: 毒瘤二合一题 -- 注意本题(及本文)使用 \(x\) 向右,\(y\) 向上的「数学坐标 ...
随机推荐
- flink dataset join笔记
1.dataset的join连接,通过key进行关联,一般情况下的join都是inner join,类似sql里的inner join key包括以下几种情况: a key expression a ...
- ORACLE ORION测试IO性能
https://www.oracle.com/technetwork/cn/topics/index-088165-zhs.html 下载地址 Orion是Oracle提供的IO性能测试工具,运行该工 ...
- Python从零开始——解释器
- Pods应用NFS存储
Volumes选择NFS服务器: 条件: 1. k8s集群,目前为(hadoop1,hadoop2,hadoop3) 2. 另起一台服务器做专门的NFS服务器 3. NFS需要在K8S的各个节点安装 ...
- Elasticsearch+x-pack认证license过期处理
介绍: x-pack是Elasticsearch中的一个plugin,主要功能是实现认证功能 报错: 类似下面这样的报错,是因为licese过期导致的 解决: 1.首先去Elasticsearch的官 ...
- AI-数据标注类型
随着数据的暴增和计算机硬件技术的发展,也催生了AI技术在各行各业的应用渗透.而想将AI技术应用到各行各业,数据是必需品.因为数据直接影响到AI最终训练出来的模型好坏.AI建模没有太大门槛,但数 ...
- MATLAB读取一个文件夹下的多个子文件夹中的多个指定格式的文件
MATLAB需要读取一个文件夹下的多个子文件夹中的指定格式文件,这里以读取*.JPG格式的文件为例 1.首先确定包含多个子文件夹的总文件夹 maindir = 'C:\Temp Folder'; 2. ...
- 玩转jmeter:beanshell必备技能
beanshell是什么 BeanShell是一个小型嵌入式Java源代码解释器,具有对象脚本语言特性,能够动态地执行标准JAVA语法,并利用在JavaScript和Perl中常见的的松散类型.命令. ...
- 【java】定时任务停止时间设置
我的需求:每天9点至20点运行 解决办法:@Scheduled(cron = "0 * 9,10,11,12,13,14,15,16,17,18,19 * * ?") 解释: 逗 ...
- vue+element table的弹窗组件
在处理表格编辑相关的需求,是需要做一个弹框进行保存的:或者查看表格数据的详细信息时,也是需要做弹窗: 当然 ,这是类似于这样的 ,当然 element 已经帮我们做好 弹窗这一块 主要 我想记录的是 ...