本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:UOJ132

正解:DP+上下界网络流

解题报告:

  第一、二问是一起的,DP一遍可以解决。

  具体而言,f[i]记录到达i的最优值,g[i]记录前驱结点。

  按y分层,不同层之间直接转,左上右上的一条直线上的点x、y坐标的和或者差相等,map保存最后的值(写转入会方便一些)。

  同一层之间有一点麻烦,考虑一定是从一个点走进这一层之后,把一边的所有点遍历完之后再走向另一边,从某个点走出这一层。

  这个用前缀和后缀最大值维护一下就可以了,输出方案的时候就记录一下这个点是从同一层还是从之前的层转移过来的,就可以确定经过的点了。

  第三问的话,我们可以通过前两问,得到每个点的f,对于可能出现在最优解上的边,我们都拎出来,然后建图。

  考虑题目要求我们什么。因为每条边都至少要被经过一次,就可以理解成求下界为1上界为inf的最小流。

  就变成了有上下界的最小流问题了,这道题的建图方式是:超级源点S向所有in[i]-out[i]>0的点连容量为in[i]-out[i]的边,所有in[i]-out[i]<0的点向超级汇点T连容量为out[i]-in[i]的边,原边容量为inf。

  跑一遍最大流,最后用满流-最大流即为所求。

  

  ps:我开始是用vector存下所有的可行转移,然后调了一个晚上调不出,小点都过了,大点有的AC有的WA...

    今天早上一来机房,痛下决心,task3重写!

    换成了重新DP一遍,倒着做一遍,再正着连边,这样做就好调很多了...

    建议:不要在脑子不清醒的时候写这道题,这会让你怀疑自己长了一个假脑子...  

      如果发现自己调试不出来了,建议换一种写法或者重写一遍。祝你身体健康

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
typedef long long LL;
const int MAXN = 50011;
const int MAXM = 300011;
const int MOD = 300007;
const int inf = (1<<29);
int n,f[MAXN],g[MAXN],from[MAXN],from2[MAXN],ans,dui[MAXN],belong[MAXN],le[MAXN],ri[MAXN],ycnt;
int ecnt,first[MAXN],in[MAXN],hcnt,S,T,deep[MAXN],tot,dp[MAXN];
bool same[MAXN],vis[MAXN];
map<int,bool>mp[MAXN];
struct node{ int x,y,id; }a[MAXN];
struct edge{ int to,next,f; }e[MAXM];
struct bian{ int x,y; }b[MAXM];
inline bool cmpy(node q,node qq){ if(q.y==qq.y) return q.x<qq.x; return q.y<qq.y; }
inline void link(int x,int y,int z){
e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z;
e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0;
} inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} struct Hash{//哈希表查询直线上的最近的点
int ecnt,first[MOD+12],to[100011],next[100011],w[100011];
inline void clear(){ ecnt=0; memset(first,0,sizeof(first)); }
inline int query(int x){ int cc=x%MOD; cc+=MOD; cc%=MOD; for(int i=first[cc];i;i=next[i]) if(to[i]==x) return w[i]; return -1; }
inline void insert(int x,int val) {
int cc=x%MOD; cc+=MOD; cc%=MOD; for(int i=first[cc];i;i=next[i]) if(to[i]==x) { w[i]=val; return ; }
next[++ecnt]=first[cc]; first[cc]=ecnt; to[ecnt]=x; w[ecnt]=val;
}
}t1,t2,t3; inline void DP(){//第一问输出最优值
int l,r,last,pos; t1.insert(0,0); t2.insert(0,0); t3.insert(0,0);
for(int i=1;i<=n;i++) f[i]=g[i]=-inf;
for(int i=1;i<=n;i=r+1) {
l=r=i; while(r<n && a[r+1].y==a[l].y) r++;
ycnt++; le[ycnt]=l; ri[ycnt]=r; for(int j=l;j<=r;j++) belong[j]=ycnt;
//转入
for(int j=l;j<=r;j++) {
last=t1.query(a[j].x+a[j].y); t1.insert(a[j].x+a[j].y,j); if(last==-1) continue;
if(f[last]+1>f[j]) { f[j]=f[last]+1; from[j]=last; }
}
for(int j=l;j<=r;j++) {
last=t2.query(a[j].x-a[j].y); t2.insert(a[j].x-a[j].y,j); if(last==-1) continue;
if(f[last]+1>f[j]) { f[j]=f[last]+1; from[j]=last; }
}
for(int j=l;j<=r;j++) {
last=t3.query(a[j].x); t3.insert(a[j].x,j); if(last==-1) continue;
if(f[last]+1>f[j]) { f[j]=f[last]+1; from[j]=last; }
} //同层之间的转移,一定是从pos走入,然后走到边界,再往回走,从某个点走出
pos=l;//存储最大的位置,前缀max
for(int j=l+1;j<=r;j++) {
if(f[j-1]>f[pos]) pos=j-1;
if(f[pos]+j-l>g[j]) { g[j]=f[pos]+j-l; from2[j]=pos; }
}
pos=r;//后缀max
for(int j=r-1;j>=l;j--) {
if(f[j+1]>f[pos]) pos=j+1;
if(f[pos]+r-j>g[j]) { g[j]=f[pos]+r-j; from2[j]=pos; }
} for(int j=l;j<=r;j++)
if(f[j]<g[j]) { f[j]=g[j]; same[j]=1; }//打上同层转移标记
}
ans=-inf; for(int i=1;i<=n;i++) ans=max(ans,f[i]);
if(ans<0) { printf("0\n\n0"); exit(0); }
printf("%d\n",ans);
} inline void Print(){
int pos=1,top=0; for(int i=1;i<=n;i++) if(f[i]>f[pos]) pos=i;
while(pos) {//逆序输出
if(same[pos]) {
if(from2[pos]<pos) {
for(int i=pos;i>from2[pos];i--) dui[++top]=i;
for(int i=le[belong[pos]];i<=from2[pos];i++) dui[++top]=i;
}
else{
for(int i=pos;i<from2[pos];i++) dui[++top]=i;
for(int i=ri[belong[pos]];i>=from2[pos];i--) dui[++top]=i;
}
pos=from2[pos];
pos=from[pos];
}
else { dui[++top]=pos; pos=from[pos]; }
}
for(int i=top;i>=1;i--) printf("%d ",a[dui[i]].id); puts("");
} inline void DP2(){//逆序跑一遍,方便连边
for(int i=0;i<=n;i++) if(f[i]==ans) dp[i]=1; else dp[i]=-inf;
for(int i=0;i<=n;i++) g[i]=-inf;
t1.clear(); t2.clear(); t3.clear(); int l,r,last,pos; dp[0]=ans;
for(int i=n;i>=1;i=l-1) {
l=r=i; while(a[l-1].y==a[r].y) l--;
for(int j=l;j<=r;j++) {
last=t1.query(a[j].x+a[j].y); t1.insert(a[j].x+a[j].y,j); if(last==-1) continue;
if(dp[last]+1>dp[j]) { dp[j]=dp[last]+1; from[j]=last; }
}
for(int j=l;j<=r;j++) {
last=t2.query(a[j].x-a[j].y); t2.insert(a[j].x-a[j].y,j); if(last==-1) continue;
if(dp[last]+1>dp[j]) { dp[j]=dp[last]+1; from[j]=last; }
}
for(int j=l;j<=r;j++) {
last=t3.query(a[j].x); t3.insert(a[j].x,j); if(last==-1) continue;
if(dp[last]+1>dp[j]) { dp[j]=dp[last]+1; from[j]=last; }
}
pos=l;
for(int j=l+1;j<=r;j++) {
if(dp[pos]+r-pos<dp[j-1]+r-j+1) pos=j-1;
if(dp[pos]+r-pos>g[j]) g[j]=dp[pos]+r-pos;
}
pos=r;
for(int j=r-1;j>=l;j--) {
if(dp[pos]+pos-l<dp[j+1]+j+1-l) pos=j+1;
if(dp[pos]+pos-l>g[j]) g[j]=dp[pos]+pos-l;
}
for(int j=l;j<=r;j++) dp[j]=max(dp[j],g[j]);
}
} inline void build(){//建图
t1.clear(); t2.clear(); t3.clear(); a[0].x=a[0].y=0; int l,r,last;
for(int i=n;i>=0;i=l-1) {
l=le[belong[i]]; r=ri[belong[i]];
for(int j=l;j<=r;j++) {
last=t1.query(a[j].x+a[j].y); t1.insert(a[j].x+a[j].y,j); if(last==-1) continue;
if(dp[last]+f[j]==ans) b[++hcnt].x=j,b[hcnt].y=last;
}
for(int j=l;j<=r;j++) {
last=t2.query(a[j].x-a[j].y); t2.insert(a[j].x-a[j].y,j); if(last==-1) continue;
if(dp[last]+f[j]==ans) b[++hcnt].x=j,b[hcnt].y=last;
}
for(int j=l;j<=r;j++) {
last=t3.query(a[j].x); t3.insert(a[j].x,j); if(last==-1) continue;
if(dp[last]+f[j]==ans) b[++hcnt].x=j,b[hcnt].y=last;
}
} ecnt=1; S=n+1; T=S+1;
for(int i=1;i<=hcnt;i++) { in[b[i].x]--; in[b[i].y]++; link(b[i].x,b[i].y,inf); }
for(int i=1;i<=n;i++) {
if(in[i]>0) link(S,i,in[i]),tot+=in[i];
else if(in[i]<0) link(i,T,-in[i]);
}
} inline bool bfs(){
int head,tail,u; head=tail=0;
for(int i=1;i<=T;i++) deep[i]=-1; deep[S]=0; dui[++tail]=S;
while(head<tail) {
u=dui[++head];
for(int i=first[u];i;i=e[i].next) {
if(e[i].f==0) continue; int v=e[i].to; if(deep[v]!=-1) continue;
deep[v]=deep[u]+1; dui[++tail]=v;
}
}
if(deep[T]==-1) return false;
return true;
} inline int dinic(int x,int remain){
if(x==T || remain==0) return remain;
int flow=0,ff;
for(int i=first[x];i;i=e[i].next) {
if(e[i].f==0) continue; int v=e[i].to;
if(deep[v]!=deep[x]+1) continue;
ff=dinic(v,min(remain,e[i].f));
if(ff==0) deep[v]=-1;
else {
flow+=ff; remain-=ff;
e[i].f-=ff; e[i^1].f+=ff;
if(remain==0) return flow;
}
}
return flow;
} inline void work(){
n=getint(); for(int i=1;i<=n;i++) { a[i].x=getint(); a[i].y=getint(); a[i].id=i; }
sort(a+1,a+n+1,cmpy); a[0].x=a[0].y=a[n+1].x=a[n+1].y=-inf;
DP();
Print();
DP2();
build();
while(bfs())
tot-=dinic(S,inf);
printf("%d",tot);
} int main()
{
work();
return 0;
}

  

UOJ132 【NOI2015】小园丁与老司机的更多相关文章

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

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

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

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

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

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

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

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

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

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

  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≤ ...

  10. 【洛谷2304_LOJ2134】[NOI2015]小园丁与老司机(动态规划_网络流)

    题目: 洛谷 2304 LOJ 2134 (LOJ 上每个测试点有部分分) 写了快一天 -- 好菜啊 分析: 毒瘤二合一题 -- 注意本题(及本文)使用 \(x\) 向右,\(y\) 向上的「数学坐标 ...

随机推荐

  1. 父类virtual和overload,子类reintroduce; overload;

    如果函数在父类中既是虚拟方法也是重载方法,那么:TBase=class(TObject)function DisappearRoutinel: String; overload; virtual;fu ...

  2. 我的Android进阶之旅------>Android 众多的布局属性详解

    Android功能强大,界面华丽,但是众多的布局属性就害苦了开发者,下面这篇文章结合了网上不少资料,希望对读者有用. 第一类:属性值为true或false android:layout_centerH ...

  3. ORACLE中RECORD、VARRAY、TABLE的使用具体解释

     1     说明 1.1       RECORD 定义记录数据类型. 它类似于C语言中的结构数据类型(STRUCTURE).PL/SQL提供了将几个相关的.分离的.基本数据类型的变量组成一个总 ...

  4. json & pickle & shelve 模块

    JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下: # json序列化 import json,time user={'name':'egon' ...

  5. 安装mysql以及修改初始密码

    我们可以采用类似安全模式的方法修改初始密码 先执行命令  mysqld_safe --skip-grant-tables &   (设置成安全模式) &,表示在后台运行,不再后台运行的 ...

  6. 江卓尔与比特币增发,谣言or先知-千氪

    最近,很多圈内人都在讨论比特币是否应该增发,但等等,比特币真的会增发吗?到底是真有增发计划还是某些人别有用心地在散布谣言? 那么消息是从哪里出来的?北京时间 2 月 10 日晚,莱比特矿池创始人江卓尔 ...

  7. equal?, == and eql?, ===,

    1.BasicObject中定义了 == 和equal?这两个方法,两个方法等价,用来比较两个对象是否是同一个对象,是的话结果就为true. 既然两者相同,为何要定义两个呢?只是为了再命名一个别名吗? ...

  8. JavaScript:基础扩展(1)——JSON

    JavaScript:扩展知识(1)——JSON 理解: 关于 JSON,最重要的是要理解它是一种数据格式,不是一种编程语言.虽然具有相同的语法形式,但 JSON 并不从属于 JavaScript.而 ...

  9. try catch 事务不会滚

    在spring机制中,在配置事务后,如果采用try catch 捕获异常后,因为异常已经被捕获,所以事务不会滚,从而产生许多脏数据.解决办法: 1.在catch中抛出异常,(throw new Run ...

  10. iOS 反射 学习 和 运用

    iOS  反射 学习 和 运用 反射:  通过 类名来获得生成的相应的类的实例 的这种机制  叫 反射 常用的反射方式 把 NSDictionary  转成 自定义 model 自定义 model 转 ...