[NOI2015]小园丁与老司机(DP+上下界最小流)
由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法。
先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现。
第一问:以行为阶段,对于每行,暴力枚举最有路径在这行上的起点和终点,g[x]记录当这行的最优路径以x为起点时,终点应在什么位置,f[x]记录走到x且这一行以x为起点,之后最多还能走到多少个点。
第二问:由于当一行的起点和终点都确定后,决策也是确定的,故只需要沿着DFS一遍即可得到最优路径。
第三问:先考虑怎么找到所有可能在最优路径上的边,同样暴力枚举每行的起点和终点即可,注意处理(0,0)的情况。找到符合要求的边后,问题就是用最少的路径数覆盖这些边,使用有上下界的最小流求解。
#include<map>
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std; const int N=,M=,inf=;
bool va[N],vb[N];
int n,mx,cnt=,s0,ss,tt,S,T,d[N],f[N],h[N],dis[N],idx[N],q[N],cur[N],a[N],mp[N][];
int Lst[N],Nxt[N],pre[N],suf[N],premx[N],sufmx[N],to[M],fl[M],nxt[M],Do[N],Do2[N];
struct P{ int x,y; }p[N]; bool cmp1(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y<p[b].y; }
bool cmp2(int a,int b){ return (p[a].x==p[b].x) ? p[a].y<p[b].y : p[a].x<p[b].x; }
bool cmp3(int a,int b){ return (p[a].x-p[a].y==p[b].x-p[b].y) ? p[a].y<p[b].y : p[a].x-p[a].y<p[b].x-p[b].y; }
bool cmp4(int a,int b){ return (p[a].x+p[a].y==p[b].x+p[b].y) ? p[a].y<p[b].y : p[a].x+p[a].y<p[b].x+p[b].y; }
bool cmp5(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y>p[b].y; } inline void add(int u,int v,int w){
to[++cnt]=v; fl[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt;
to[++cnt]=u; fl[cnt]=; nxt[cnt]=h[v]; h[v]=cnt;
} void work(int u,int v,int low,int high){ add(u,v,high-low); d[u]-=low; d[v]+=low; } bool bfs(){
rep(i,,n+) dis[i]=; dis[S]=; q[]=S;
for (int st=,ed=; st<ed; ){
int x=q[++st];
For(i,x) if (fl[i] && !dis[k=to[i]]) dis[k]=dis[x]+,q[++ed]=k;
}
return dis[T];
} int dfs(int x,int lim){
if (x==T) return lim;
int c=;
for (int i=cur[x],k; i; i=nxt[i])
if (fl[i] && dis[k=to[i]]==dis[x]+){
int t=dfs(k,min(lim-c,fl[i]));
c+=t; fl[i]-=t; fl[i^]+=t;
if (fl[i]) cur[x]=i;
if (c==lim) return lim;
}
if (!c) dis[x]=-;
return c;
} int dinic(){
int res=;
while (bfs()){
rep(i,,n+) cur[i]=h[i];
res+=dfs(S,inf);
}
return res;
} void Print(int x){
if (!x) return;
printf("%d ",x);
if (Do[x]==) { Print(mp[x][Do2[x]]); return; }
if (idx[x]>idx[Do[x]]){
int t=Do[x];
for (int i=Nxt[x]; i; i=Nxt[i]) printf("%d ",i);
for (x=Lst[x]; x!=t; x=Lst[x]) printf("%d ",x);
printf("%d ",t); Print(mp[t][Do2[t]]); return;
}
if (idx[x]<idx[Do[x]]){
int t=Do[x];
for (int i=Lst[x]; i; i=Lst[i]) printf("%d ",i);
for (x=Nxt[x]; x!=t; x=Nxt[x]) printf("%d ",x);
printf("%d ",t); Print(mp[t][Do2[t]]); return;
}
} void solve1(){
rep(i,,n) a[i]=i;
sort(a+,a+n+,cmp1);
rep(i,,n) if (p[a[i-]].y==p[a[i]].y) Lst[a[i]]=a[i-],Nxt[a[i-]]=a[i];
sort(a+,a+n+,cmp2);
rep(i,,n) if (p[a[i-]].x==p[a[i]].x) mp[a[i-]][]=a[i];
sort(a+,a+n+,cmp3);
rep(i,,n) if (p[a[i-]].x-p[a[i-]].y==p[a[i]].x-p[a[i]].y) mp[a[i-]][]=a[i];
sort(a+,a+n+,cmp4);
rep(i,,n) if (p[a[i-]].x+p[a[i-]].y==p[a[i]].x+p[a[i]].y) mp[a[i-]][]=a[i];
sort(a+,a+n+,cmp5);
rep(i,,n) idx[a[i]]=i;
} void W(int x){
rep(i,,) if (mp[x][i]){
int to=mp[x][i];
if (f[to]+>f[x]) Do2[x]=i,f[x]=f[to]+;
}
} int calc(int x,int y){
return (idx[x]<idx[y]) ? pre[y]+f[mp[y][Do2[y]]] : suf[y]+f[mp[y][Do2[y]]];
} bool jud(P &p){ return p.x== || p.x==p.y || p.x==-p.y; } void solve2(){
rep(k,,n) if (p[a[k]].y!=p[a[k-]].y) pre[a[k]]=; else pre[a[k]]=pre[a[k-]]+;
for (int k=n; k; k--) if (p[a[k]].y!=p[a[k+]].y) suf[a[k]]=; else suf[a[k]]=suf[a[k+]]+;
rep(k,,n) if (p[a[k]].y!=p[a[k-]].y){
for (int x=a[k]; x; x=Nxt[x]) f[x]=,W(x);
for (int x=a[k]; x; x=Nxt[x])
for (int y=a[k]; y; y=Nxt[y])
if (y!=x && calc(x,y)>f[x]) f[x]=calc(x,y),Do[x]=y;
}
rep(i,,n) if (jud(p[i])) mx=max(mx,f[i]);
printf("%d\n",mx);
rep(i,,n) if (f[i]==mx && jud(p[i])) { Print(i); break; }
puts("");
} int solve3(){
rep(i,,n) if (f[i]==mx && jud(p[i])) work(s0,i,,inf),va[i]=;
for (int i=n; i; i--) if (p[a[i]].y!=p[a[i+]].y){
for (int x=a[i]; x; x=Lst[x]) if (va[x]){
if (f[mp[x][Do2[x]]]+==f[x]) vb[x]=;
for (int y=a[i]; y; y=Lst[y]) if (y!=x && calc(x,y)==f[x]) vb[y]=;
}
for (int x=a[i]; x; x=Lst[x])
if (vb[x]) rep(j,,) if (mp[x][j] && f[mp[x][j]]==f[mp[x][Do2[x]]])
va[mp[x][j]]=,work(x,mp[x][j],,inf);
}
rep(i,,n+) work(ss,i,,inf),work(i,tt,,inf);
rep(i,,n+) if (d[i]>) add(S,i,d[i]); else add(i,T,-d[i]);
add(tt,ss,inf); dinic(); S=tt; T=ss; return inf-dinic();
} int main(){
freopen("driver.in","r",stdin);
freopen("driver.out","w",stdout);
scanf("%d",&n); s0=n+; ss=n+; tt=n+; S=n+; T=n+;
rep(i,,n) scanf("%d%d",&p[i].x,&p[i].y);
solve1(); solve2(); printf("%d\n",solve3());
return ;
}
[NOI2015]小园丁与老司机(DP+上下界最小流)的更多相关文章
- luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
[BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...
- [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\) 棵许愿 ...
- bzoj 4200: [Noi2015]小园丁与老司机【dp+有上下界最小流】
洛谷上有个点死活卡不过去,不知道是哪里写丑了orz 参考:https://www.cnblogs.com/ditoly/p/BZOJ4200.html 从上往下dp,设f为不向左右走直接上去的值,g为 ...
- BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...
- [Noi2015]小园丁和老司机
来自FallDream的博客,未经允许,请勿转载,谢谢. 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有n棵许愿树,编号1,2,3,…,n,每棵树可以看作平面上的一个点,其中 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- 并不对劲的loj2134:uoj132:p2304:[NOI2015]小园丁与老司机
题目大意 给出平面直角坐标系中\(n\)(\(n\leq5*10^4\))个点,第\(i\)个点的坐标是\(x_i,y_i(|x_i|\leq10^9,1\leq y_i\leq10^9)\),只有朝 ...
随机推荐
- 【vijos】P1083 小白逛公园
[算法]线段树 [题解] 学自:https://vijos.org/p/1083/solution(wang_yanheng的回答) 回溯时维护一段区间的以下域: sumL:从左端点起连续区间的最大和 ...
- 所有和Java中代理有关的知识点都在这了。
对于每一个Java开发来说,代理这个词或多或少都会听说过.你可能听到过的有代理模式.动态代理.反向代理等.那么,到底什么是代理,这么多代理又有什么区别呢.本文就来简要分析一下. 代理技术,其实不只是J ...
- 解决java在对MySQL插入数据时出现乱码问题
1.在连接数据库的时候请注意, 最使用连接连接数据库的时候,必须在后面追加上编码的设置:useUnicode=true&characterEncoding=UTF-8,如下图所示. 参考连接: ...
- 伪病毒 Rp_test
第一个写的对电脑有破坏性的程序= =,然后发现写system的copy的时候不会用字符串替代路径,然后就萎了= =,只能写一个没有自身复制的伪病毒了,坑到了好多同学的电脑,23333.... //By ...
- Coursera在线学习---第八节.K-means聚类算法与主成分分析(PCA)
一.K-means聚类中心初始化问题. 1)随机初始化各个簇类的中心,进行迭代,直到收敛,并计算代价函数J. 如果k=2~10,可以进行上述步骤100次,并分别计算代价函数J,选取J值最小的一种聚类情 ...
- perl6 拖库脚本
#注入点: #https://fei.sg/shop/products.php?action=content&id=-23 #check mysql column_name of the sq ...
- Python3 PyPAML 模块(配置文件的操作)
YAML 是专门用来写配置文件的语言,非常简洁和强大 它的基本语法规则如下: 1.大小写敏感 2.使用缩进表示层级关系 3.缩进时不允许使用Tab键,只允许使用空格. 4.缩进的空格数目不重要,只要相 ...
- 【bzoj3682】Phorni
后缀平衡树裸题. 后缀平衡树呢,实际上是一个很naive的东西.就是用平衡树维护后缀数组. 这样的话就可以支持在最前端插入一个字符(相当于插入新的后缀) 每次比较节点的tag是O(1)的,所以可以快速 ...
- BZOJ 4241: 历史研究——莫队 二叉堆
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4241 题意:N个int范围内的数,M次询问一个区间最大的(数字*出现次数)(加权众数),可以 ...
- VPS速度测试(4):上传下载速度、服务器带宽、Ping响应时间
1.VPS的速度好坏经常是我们选择某一个VPS商家的重要参考指标,对于国外的VPS主机我们可以执行以下命令来测试VPS入口带宽是多少. wget https://cachefly.cachefly.n ...