[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]小园丁与老司机的更多相关文章

  1. [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会

    [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...

  2. [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程

    [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...

  3. [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分

    [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...

  4. [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士

    [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...

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

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

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

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

  7. LOJ#2134 小园丁与老司机

    我的妈呀,这码农神题...... 第一问是个DP,要记录方案.先把纵向的转移建图.发现可以按照y坐标来划分阶段,每一层vector存一下,用前后缀最大值来转移. 第二问考虑所有可能成为最优方案的边.从 ...

  8. UOJ#132&bzoj4200[Noi2015]小园丁与老司机

    看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...

  9. *LOJ#2134. 「NOI2015」小园丁与老司机

    $n \leq 5e4$个平面上的点,从原点出发,能从当前点向左.右.上.左上或右上到达该方向最近的给定点.问三个问:一.最多经过多少点:二.前一问的方案:三.其所有方案种非左右走的边至少要开几辆挖掘 ...

随机推荐

  1. Centos7升级gcc极简教程

    centos7默认gcc版本为4.8,一般不满足编译需求,因此升级gcc版本为常见操作: 现有博客中,大多数教程都是基于源码重新编译安装:但是源码编译过程等待时间很长且编译麻烦. 因此,直接基于命令升 ...

  2. Android开发笔记:Android开发环境搭建

    基于Eclipse开发 1. 安装JDK 首先进入JDK下载页面,选择需要的版本下载安装. JDK 下载地址:https://www.oracle.com/technetwork/java/javas ...

  3. 用canvas实现手写签名功能

    最近开发网站有一个需求,要求页面上有一块区域,用户能用鼠标在上面写字,并能保存成图片 base64 码放在服务器.这样的需求用 canvas 实现是最好的.需要用到 canvas 的以下几个属性: b ...

  4. Codeforces Round #599 (Div. 1) C. Sum Balance 图论 dp

    C. Sum Balance Ujan has a lot of numbers in his boxes. He likes order and balance, so he decided to ...

  5. Python进阶实战之三级菜单

    目录 一.Python进阶实战之三级菜单 1.1 面条版 1.2 文艺青年版 一.Python进阶实战之三级菜单 打印省.市.县三级菜单 可返回上一级 可随时退出程序 1.1 面条版 menu = { ...

  6. windows7 php 环境架设

    参考 https://www.jb51.net/article/38048.htm   常见问题解决方案     https://blog.csdn.net/w_yunlong/article/det ...

  7. Python连载44-XML其他注意点

    一.XML文件注意点 1.内容中不能出现尖括号 例如:下面是不合法的 <grade>成绩<90</grade> 解决方案:使用实体引用<EntityReferenc ...

  8. CF 704 D. Captain America

    CF 704 D. Captain America 题目链接 题目大意:给出\(n\)个点的坐标,你要将每个点染成红色或者蓝色.染一个红色要付出\(r\)的代价,染一个蓝色要付出\(b\)的代价.有\ ...

  9. idea配置pyspark

    默认python已经配好,并已经导入idea,只剩下pyspark的安装 1.解压spark-2.1.0-bin-hadoop2.7放入磁盘目录 D:\spark-2.1.0-bin-hadoop2. ...

  10. 洛谷 P2602(数位DP)

    ### 洛谷 P2602 题目链接 ### 题目大意:给你一个区间,问你区间所有数字中,0.1.2 .... 9 的个数的总和分别为多少. 分析: 枚举 0 ~ 9 进行数位 DP 即可. 注意记忆化 ...