[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
题意
给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题.
第一个问题是从原点 \((0,0)\) 出发, 在只能向←↖↑↗→五个方向中有未到达的点的方向走且在没有到达一个点的时候不能中途转弯的情况下最多能到达的点数, 并输出一种可行方案.
第二个问题是如果用若干可以从任意点出发但是只能向↖↑↗方向沿着所有可能出现在最优解的直线上走的压路机将所有可能出现在最优解上的边都走过至少一遍所需要的最少的压路机数量.
\(n\le 50000,|x_i|\le 10^9,0<y_i\le 10^9\)
题解
农业题真tm劲啊
第一个问题相当于一个有特殊限制的分层图最长路.
显然走的时候 \(y\) 值是单调不降的, 我们可以从小到大枚举 \(y\) 坐标分层计算.
同一层的时候假设在 \(s\) 处进入, 从 \(t\) 处离开, 那么最优策略一定是先走到这一层中对 \(t\) 的对侧端点然后再回来. 我们设 \(f^{[1]}_i\) 为从原点走到 \(i\) 的最长路, \(f^{[2]}_i\) 为上一层中可以走到点 \(i\) 的点中最大的 \(f^{[1]}\), 即进入当前层之前的最长路.
因为↖↑↗三个方向能移动到的位置分别保持 \(x+y\), \(x\), \(x-y\) 相等, 于是可以全局维护一个哈希表或者 map
来维护 \(f^{[2]}_i\). 如果以前没有出现过相等位置则贡献为 \(-\infty\). 原点的贡献为 \(0\).
从 \(f^{[2]}\) 计算 \(f^{[1]}\) 则可以分类讨论, \(s=t\) 时是平凡情况, 注意这里到达后这个点会变成已到达的, 如果要访问同一层的其他点就会回不来, 于是这里直接等于 \(f^{[2]}_i+1\). 剩下的情况一个是 \(s<t\), 一个是 \(s>t\).
\(s<t\) 的时候可以访问到 \(t\) 左侧的所有点, \(s>t\) 的时候可以访问到 \(t\) 右侧的所有点, 显然这个贡献和 \(s\) 无关, 正反扫一遍记录一下前后缀最大的 \(f^{[2]}_i\) 就好了.
以上贡献的时候都需要维护一个 pair
, 第一关键字是DP值, 第二关键字是来源. 用 pair
可以节省额外的记录方案代码. 注意 \(f^{[1]}_i\) 的来源记录的是同层的 \(s\) 的位置, \(f^{[2]}_i\) 的来源记录的是上一层的 \(t\) 的位置.
输出方案的时候处理一下同一层的特判一下 \(s=t\) 的情况第一个问题就做完了.
第二个问题依然比较恶心
下界最小流的模型很显然就不说了. 关键在于把这个图建出来. 因为它问的是所有可能出现在最优解的直线.
如果 \(u\) 沿↖↑↗三个方向可以到达 \(v\), 那么只要原点到 \(u\) 的最长路和 \(v\) 出发的最长路之和与第一问的答案相等就可以出现在最优解上. 那么就需要计算出从某个点出发的最长路的长度.
这个DP和第一问类似, 不再赘述. 不过这次可以在任意位置结束, 所以没有后继的时候贡献是 \(0\) 而不是 \(-\infty\).
最后注意一下不要写假Dinic...这题数据范围比较大, 假复杂度的Dinic很容易被卡...我猜Po姐就是因为写了假Dinic才没有阿克Day2的然而神仙Po姐实际上写了个费用流
参考代码
一杯茶, 一包烟, 一个破题调一天
#include <bits/stdc++.h>
const int MAXV=50010;
const int MAXN=50010;
const int MAXE=1e7+10;
const int INF=0x7FFFFFFF;
typedef std::pair<int,int> Pair;
struct Edge{
int from;
int to;
int flow;
bool blk;
Edge* rev;
Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* cur[MAXV];
Edge* top=E;
int n;
int ycnt;
int s[MAXN];
Pair a[MAXN];
int id[MAXN];
int bk1[MAXN];
int bk2[MAXN];
Pair fw1[MAXN];
Pair fw2[MAXN];
int depth[MAXV];
std::map<int,int> bkX;
std::map<int,int> bkN; // x+y fixed
std::map<int,int> bkZ; // x-y fixed
std::map<int,Pair> fwX;
std::map<int,Pair> fwN; // x+y fixed
std::map<int,Pair> fwZ; // x-y fixed
std::vector<Pair> pos[MAXN];
std::pair<Pair,int> P[MAXN];
int ReadInt();
int GetB(int);
Pair GetF(int);
bool BFS(int,int);
int Dinic(int,int);
int DFS(int,int,int);
void UpdateF(int,int);
void UpdateB(int,int);
Edge* Insert(int,int,int);
int main(){
n=ReadInt();
for(int i=1;i<=n;i++){
a[i].first=P[i].first.first=ReadInt();
s[i]=a[i].second=P[i].first.second=ReadInt();
P[i].second=i;
}
std::sort(s+1,s+n+1);
std::sort(P+1,P+n+1);
ycnt=std::unique(s+1,s+n+1)-(s+1);
for(int i=1;i<=n;i++)
pos[std::lower_bound(s+1,s+ycnt+1,P[i].first.second)-s].emplace_back(P[i].first.first,P[i].second);
pos[0].emplace_back(0,0);
UpdateF(0,0);
Pair ans;
for(int i=1;i<=ycnt;i++){
for(size_t j=0;j<pos[i].size();j++){
int x=pos[i][j].second;
auto p=GetF(x);
fw2[x]=p;
fw1[x]=Pair(p.first+1,x);
}
auto maxf=Pair(INT_MIN,0);
for(size_t j=0;j<pos[i].size();j++){
int p=pos[i][j].second;
id[p]=j;
fw1[p]=std::max(fw1[p],Pair(maxf.first+j+1,maxf.second));
if(fw2[p].first>=0)
maxf=std::max(maxf,Pair(fw2[p].first,p));
}
maxf=Pair(INT_MIN,0);
for(size_t j=pos[i].size()-1;j<pos[i].size();j--){
int p=pos[i][j].second;
fw1[p]=std::max(fw1[p],Pair(maxf.first+(pos[i].size()-j),maxf.second));
if(fw2[p].first>=0)
maxf=std::max(maxf,Pair(fw2[p].first,p));
UpdateF(p,fw1[p].first);
ans=std::max(ans,Pair(fw1[p].first,p));
}
}
int bkans=0;
for(int i=ycnt;i>=1;i--){
for(size_t j=0;j<pos[i].size();j++){
int x=pos[i][j].second;
bk2[x]=GetB(x);
bk1[x]=bk2[x]+1;
}
int maxb=0;
for(size_t j=0;j<pos[i].size();j++){
int p=pos[i][j].second;
bk1[p]=std::max(bk1[p],maxb);
maxb=std::max(maxb,int(bk2[p]+(pos[i].size()-j)));
}
maxb=0;
for(size_t j=pos[i].size()-1;j<pos[i].size();j--){
int p=pos[i][j].second;
bk1[p]=std::max(bk1[p],maxb);
maxb=std::max(maxb,int(bk2[p]+j+1));
UpdateB(p,bk1[p]);
bkans=std::max(bkans,bk1[p]);
}
}
printf("%d\n",ans.first);
std::vector<int> sol;
for(int cur=ans.second;cur!=0;){
sol.push_back(cur);
int i=std::lower_bound(s+1,s+ycnt+1,a[cur].second)-s;
if(fw1[cur].second!=cur){
int next=fw1[cur].second;
if(id[next]<id[cur]){
for(int j=id[cur]-1;j>id[next];j--)
sol.push_back(pos[i][j].second);
for(int j=0;j<=id[next];j++)
sol.push_back(pos[i][j].second);
}
else{
for(int j=id[cur]+1;j<id[next];j++)
sol.push_back(pos[i][j].second);
for(int j=pos[i].size()-1;j>=id[next];j--)
sol.push_back(pos[i][j].second);
}
cur=fw1[cur].second;
}
cur=fw2[cur].second;
}
for(int i=ans.first-1;i>=0;i--)
printf("%d%c",sol[i]," \n"[i==0]);
bkX.clear();
bkN.clear();
bkZ.clear();
int ss=n+1,tt=n+2,s=n+3,t=n+4;
Edge* Ex;
std::vector<Edge*> aux;
aux.push_back(Ex=Insert(t,s,INF));
for(int i=0;i<=n;i++){
Insert(s,i,INF);
Insert(i,t,INF);
}
auto check=[=,&aux](int r,int k){
if(fw1[r].first+bk1[k]==ans.first){
Insert(r,k,INF);
aux.push_back(Insert(ss,k,1));
aux.push_back(Insert(r,tt,1));
}
};
for(int i=ycnt;i>=0;i--){
for(auto p:pos[i]){
int x,y,r=p.second;
std::tie(x,y)=a[r];
if(bkX.count(x))
check(r,bkX[x]);
if(bkN.count(x+y))
check(r,bkN[x+y]);
if(bkZ.count(x-y))
check(r,bkZ[x-y]);
bkX[x]=r;
bkN[x+y]=r;
bkZ[x-y]=r;
}
}
Dinic(ss,tt);
for(auto p:aux)
p->blk=p->rev->blk=true;
int flow=Ex->rev->flow;
flow-=Dinic(t,s);
printf("%d\n",flow);
return 0;
}
int GetB(int i){
int ans=0;
int x=a[i].first;
int y=a[i].second;
if(bkX.count(x))
ans=std::max(ans,bkX[x]);
if(bkN.count(x+y))
ans=std::max(ans,bkN[x+y]);
if(bkZ.count(x-y))
ans=std::max(ans,bkZ[x-y]);
return ans;
}
void UpdateB(int i,int d){
if(d<0)
return;
int x=a[i].first;
int y=a[i].second;
auto f=[](int& x,int d){x=std::max(x,d);};
f(bkX[x],d);
f(bkN[x+y],d);
f(bkZ[x-y],d);
}
Pair GetF(int i){
int x=a[i].first;
int y=a[i].second;
Pair ans(INT_MIN,0);
if(fwX.count(x))
ans=std::max(ans,fwX[x]);
if(fwN.count(x+y))
ans=std::max(ans,fwN[x+y]);
if(fwZ.count(x-y))
ans=std::max(ans,fwZ[x-y]);
return ans;
}
void UpdateF(int i,int d){
if(d<0)
return;
int x=a[i].first;
int y=a[i].second;
auto p=Pair(d,i);
auto f=[](Pair& x,Pair d){x=std::max(x,d);};
f(fwX[x],p);
f(fwN[x+y],p);
f(fwZ[x-y],p);
}
int Dinic(int s,int t){
int ans=0;
while(BFS(s,t))
ans+=DFS(s,INF,t);
return ans;
}
bool BFS(int s,int t){
memset(depth,0,sizeof(depth));
std::queue<int> q;
cur[s]=head[s];
depth[s]=1;
q.push(s);
while(!q.empty()){
s=q.front();
q.pop();
for(Edge* i=head[s];i!=NULL;i=i->next){
if(!i->blk&&i->flow>0&&depth[i->to]==0){
depth[i->to]=depth[s]+1;
cur[i->to]=head[i->to];
if(i->to==t)
return true;
q.push(i->to);
}
}
}
return false;
}
int DFS(int s,int flow,int t){
if(s==t||flow<=0)
return flow;
int rest=flow;
for(Edge*& i=cur[s];i!=NULL;i=i->next){
if(!i->blk&&i->flow>0&&depth[i->to]==depth[s]+1){
int tmp=DFS(i->to,std::min(rest,i->flow),t);
if(tmp<=0)
depth[i->to]=0;
rest-=tmp;
i->flow-=tmp;
i->rev->flow+=tmp;
if(rest<=0)
break;
}
}
return flow-rest;
}
inline Edge* Insert(int from,int to,int flow){
Edge* ret=top;
top->from=from;
top->to=to;
top->flow=flow;
top->blk=false;
top->rev=top+1;
top->next=head[from];
head[from]=top++;
top->from=to;
top->to=from;
top->flow=0;
top->blk=false;
top->rev=top-1;
top->next=head[to];
head[to]=top++;
return ret;
}
inline int ReadInt(){
int x=0;
int sgn=1;
register char ch=getchar();
while(!isdigit(ch)){
sgn=(ch=='-'?-sgn:sgn);
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-'0';
ch=getchar();
}
return x*sgn;
}
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机的更多相关文章
- [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会
[LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...
- [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程
[LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士
[LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- [BZOJ]4200: [Noi2015]小园丁与老司机
Time Limit: 20 Sec Memory Limit: 512 MBSec Special Judge Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维 ...
- LOJ#2134 小园丁与老司机
我的妈呀,这码农神题...... 第一问是个DP,要记录方案.先把纵向的转移建图.发现可以按照y坐标来划分阶段,每一层vector存一下,用前后缀最大值来转移. 第二问考虑所有可能成为最优方案的边.从 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- *LOJ#2134. 「NOI2015」小园丁与老司机
$n \leq 5e4$个平面上的点,从原点出发,能从当前点向左.右.上.左上或右上到达该方向最近的给定点.问三个问:一.最多经过多少点:二.前一问的方案:三.其所有方案种非左右走的边至少要开几辆挖掘 ...
随机推荐
- Eclipse maven创建web项目报错Could not resolve archetype
1.下载http://repo1.maven.org/maven2/archetype-catalog.xml 通过eclipse下载和网页下载我这里都比较慢,最后用的迅雷下载 2.将本地xml文件配 ...
- C#开发BIMFACE系列29 服务端API之获取模型数据14:获取图纸列表
系列目录 [已更新最新开发文章,点击查看详细] 一个三维模型中可能包含对应多张二维图纸列表,本篇主要介绍如何获取模型文件对应的图纸列表. 请求地址:GET https://api.bimfac ...
- python request获取ip、获取登录设备
from flask import request 获取ip request.remote_addr 获取登录设备 request.user_agent.string
- python做中学(九)定时器函数的用法
程序中,经常用到这种,就是需要固定时间执行的,或者需要每隔一段时间执行的.这里经常用的就是Timer定时器.Thread 类有一个 Timer子类,该子类可用于控制指定函数在特定时间内执行一次. 可以 ...
- new 关键字 和 newInstance() 方法的 区别
区别1: new是一个关键字,可以说是一个指令: newInstance()是一个方法,Class对象的一个方法. 区别2: new主要作用是在内存中生成一个实例,而这个类可以没有提前加载到内从中: ...
- IT兄弟连 HTML5教程 HTML5的学习线路图 第二、三阶段
第二阶段编写用户交互功能 通过第一阶段的学习虽说可以完成页面制作,但并不完美,不能算是合格的前端工程师,所以要继续学习如图1.13中的第二阶段内容.现在的Web页面都融入了大量的特效,并且多数需要与用 ...
- 11-scrapy(递归解析,post请求,日志等级,请求传参)
一.递归解析: 需求:将投诉_阳光热线问政平台中的投诉标题和状态网友以及时间爬取下来永久储存在数据库中 url:http://wz.sun0769.com/index.php/question/que ...
- 在新的电脑上的Git本地库 与远程库关联前的一些设置
由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置: 第1步:创建SSH Key.在用户主目录下(user/...),看看有没有.ssh目录,如果有,再看看这个目 ...
- elementui中的el-table中拼接两个列表字段
我们知道,在ElementUI中我们是使用下面的语法来展示列表字段的: <el-table :data="yanggbs" stripe style="width: ...
- javascript检索某个字符或字符串在源字符串中的位置(下标)
indexOf()方法 JavaScript中的String对象提供了一个indexOf(searchValue, fromIndex)方法用于检索某个字符或字符串在源字符串中第一次出现的位置(下标) ...