luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机
苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊
很久以前就写了 当时记得特别清楚
写到肚子疼..
调到胳膊疼..
ex到根不不想看的程度.
当时wa了 一直不知道哪里错了 今天又调了一下午 调出来了.
思路是这样的:
先进行分层dp dp的时候我是反着dp的 因为无论是考虑后续的方案输出还是建图.
从那些终点到起点进行dp对后续的处理带来非常大的便利.
定义\(f_i\)表示由上一层转移过来的最大值.\(w_i\)表示由同层/上一层转移过来的最大值.
之所以要\(w\)数组主要是对后续建图带来很大的便利.
不同层的转移我直接map了 当然也可以离散。
同层的转移我是利用单调性优化的 所以总复杂度应该是\(nlogn\)
考虑输出方案 需要注意一些细节 但是不难.
建图是重点。刚才是反着dp 此时可以正着建图 有点topsort的意思.
然后不断维护当前层是从哪个点出来的和进来的从而把完整的图给建立出来.
最后是最小费用流 先建一个超超级源 和 超超级汇 然后 汇源连边 跑循环流 最后再把源汇边断掉 跑汇源的最大流即可.
code
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000
#define inf 1000000000
#define ld long double
#define mod 1004535809
#define pb push_back
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define pii pair<int,int>
#define F first
#define S second
#define mk make_pair
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=50100;
int n,r,flag,ql,qr,cnt,len=1,SS,TT,l;
int vis[MAXN],mark[MAXN],vis1[MAXN],cur[MAXN],d[MAXN];
int q[MAXN],f[MAXN],g[MAXN],w[MAXN],c[MAXN];
int lin[MAXN],ver[MAXN<<3],nex[MAXN<<3],e[MAXN<<3];
struct wy
{
int x,y,id;
int L,R;
int friend operator <(wy a,wy b){return a.y==b.y?a.x<b.x:a.y>b.y;}
}t[MAXN];
struct jl{int s1,s2,s3;}s[MAXN];
map<int,pii>H1,H2,H3;
inline void get_path(int x,int v)
{
if(x!=n)printf("%d ",t[x].id);
if(w[x]==1||(v&&f[x]==1))return;
int ww=v==1?g[x]:c[x];
if(t[ww].y!=t[x].y){get_path(ww,0);return;}
else
{
if(ww>x)
{
for(int j=x-1;j;--j)
if(t[j].y==t[x].y)printf("%d ",t[j].id);
else break;
for(int j=x+1;j!=ww;++j)printf("%d ",t[j].id);
}
else
{
for(int j=x+1;j<=n;++j)
if(t[j].y==t[x].y)printf("%d ",t[j].id);
else break;
for(int j=x-1;j!=ww;--j)printf("%d ",t[j].id);
}
}
get_path(ww,1);
}
inline void add(int x,int y,int z)
{
ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;
ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0;
//if(x==3&&y==qr)cout<<x<<' '<<y<<endl;
}
//map<int,int>W[MAXN];
inline void add(int x,int y,int L,int R)
{
//if(W[x].find(y)!=W[x].end())cout<<"ww"<<endl;
d[x]-=L;d[y]+=L;
add(x,y,R-L);
}
inline int bfs(int SS,int TT)
{
for(int i=0;i<=cnt;++i)vis[i]=0,cur[i]=lin[i];
l=r=0;q[++r]=SS;vis[SS]=1;
while(++l<=r)
{
int x=q[l];
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(vis[tn]||!e[i])continue;
vis[tn]=vis[x]+1;
q[++r]=tn;
if(tn==TT)return 1;
}
}
return 0;
}
inline int dinic(int x,int TT,int flow)
{
if(x==TT)return flow;
int res=flow,k;
for(int i=cur[x];i&&res;i=nex[i])
{
cur[x]=i;
int tn=ver[i];
if(vis[x]+1==vis[tn]&&e[i])
{
k=dinic(tn,TT,min(e[i],flow));
if(!k){vis[tn]=0;continue;}
res-=k;e[i]-=k;e[i^1]+=k;
}
}
return flow-res;
}
int main()
{
//freopen("1.in","r",stdin);
cnt=n=read();
for(int i=1;i<=n;++i)
{
int x,y;
x=read();y=read();
t[i]=(wy){x,y,i};
}
t[0]=(wy){0,0};
sort(t,t+1+n);
for(int i=0;i<=n;++i)
{
if(i&&t[i].y!=t[i-1].y)//鍚岃dp
{
int maxx=-r,p;//maxx琛ㄧず鏈€澶у€?p琛ㄧず鍐崇瓥浣嶇疆
for(int j=1;j<=r;++j)
{
int tn=q[j];
w[tn]=f[tn];c[tn]=g[tn];
if(maxx+r>w[tn])
{
w[tn]=maxx+r;
c[tn]=p;
}
if(f[tn]-j>maxx)maxx=f[tn]-j,p=tn;
t[tn].L=f[tn]-j+r;
}
maxx=-r;
for(int j=r;j>=1;--j)
{
int tn=q[j];
if(maxx>w[tn])
{
w[tn]=maxx;
c[tn]=p;
}
if(f[tn]+j-1>maxx)maxx=f[tn]+j-1,p=tn;
t[tn].R=f[tn]+j-1;
int x=t[tn].x,y=t[tn].y;
H1[x+y]=mk(w[tn],tn);H2[x]=mk(w[tn],tn);H3[y-x]=mk(w[tn],tn);
}
r=0;
}
//if(i==n)cout<<"ww"<<endl;
q[++r]=i;
int x=t[i].x;int y=t[i].y;
pii w1=H1[x+y];s[i].s1=w1.F==0?-1:w1.S;//宸︿笂
if(w1.F+1>f[i])f[i]=w1.F+1,g[i]=w1.S;
w1=H2[x];s[i].s2=w1.F==0?-1:w1.S;//涓婃柟
if(w1.F+1>f[i])f[i]=w1.F+1,g[i]=w1.S;
w1=H3[y-x];s[i].s3=w1.F==0?-1:w1.S;//鍙充笂
if(w1.F+1>f[i])f[i]=w1.F+1,g[i]=w1.S;
}
printf("%d\n",f[n]-1);w[n]=f[n];
//rep(1,n,i)put(f[i]);
get_path(n,1);puts("");
ql=++cnt;qr=++cnt;
add(ql,n,INF);
vis[n]=1;r=0;
for(int i=n;i>=0;--i)
{
q[++r]=i;
if(i==0||t[i].y!=t[i-1].y)
{
for(int j=1;j<=r;++j)
{
int tn=q[j];
if(mark[t[tn].L])vis1[tn]=1;
if(vis[tn])
{
mark[w[tn]]=1;
add(tn,qr,INF);
}
}
for(int j=1;j<=r;++j)mark[w[q[j]]]=0;
for(int j=r;j>=1;--j)
{
int tn=q[j];
if(mark[t[tn].R])vis1[tn]=1;
if(vis[tn])mark[w[tn]]=1;
}
for(int j=1;j<=r;++j)
{
int tn=q[j];
mark[w[tn]]=0;
if(vis[tn]&&f[tn]==w[tn])vis1[tn]=1;
if(vis1[tn])add(ql,tn,INF);
if(!vis1[tn])continue;
int s1=s[tn].s1;
int s2=s[tn].s2;
int s3=s[tn].s3;
if(s1==-1&&s2==-1&&s3==-1){add(tn,qr,INF);continue;}
if(s1!=-1&&w[s1]==f[tn]-1){vis[s1]=1;add(tn,s1,1,INF);}
if(s2!=-1&&w[s2]==f[tn]-1){vis[s2]=1;add(tn,s2,1,INF);}
if(s3!=-1&&w[s3]==f[tn]-1){vis[s3]=1;add(tn,s3,1,INF);}
}
r=0;
}
}
SS=++cnt;TT=++cnt;
int flow,sum=0;
for(int i=0;i<=cnt-2;++i)
{
if(d[i]>0)add(SS,i,d[i]);
if(d[i]<0)add(i,TT,-d[i]);
}
//cout<<ans<<endl;
add(qr,ql,INF);
while(bfs(SS,TT))
while((flow=dinic(SS,TT,inf)))sum-=flow;
sum=e[len];e[len^1]=0;
while(bfs(qr,ql))while((flow=dinic(qr,ql,inf)))sum-=flow;
printf("%d\n",sum);
return 0;
}
luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流的更多相关文章
- [NOI2015]小园丁与老司机(DP+上下界最小流)
由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法. 先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现. 第一问:以行为阶段,对于每行,暴力枚举最有路径 ...
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
[BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...
- uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】
题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...
- 【洛谷2304_LOJ2134】[NOI2015]小园丁与老司机(动态规划_网络流)
题目: 洛谷 2304 LOJ 2134 (LOJ 上每个测试点有部分分) 写了快一天 -- 好菜啊 分析: 毒瘤二合一题 -- 注意本题(及本文)使用 \(x\) 向右,\(y\) 向上的「数学坐标 ...
- 并不对劲的loj2134:uoj132:p2304:[NOI2015]小园丁与老司机
题目大意 给出平面直角坐标系中\(n\)(\(n\leq5*10^4\))个点,第\(i\)个点的坐标是\(x_i,y_i(|x_i|\leq10^9,1\leq y_i\leq10^9)\),只有朝 ...
- [BZOJ4200][Noi2015]小园丁与老司机
4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 106 Solved ...
- 【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
[BZOJ4200][NOI2015]小园丁与老司机(动态规划,网络流) 题面 BZOJ权限题,洛谷链接 题解 一道二合一的题目 考虑第一问. 先考虑如何计算六个方向上的第一个点. 左右上很好考虑,只 ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- BZOJ4200 & 洛谷2304 & UOJ132:[NOI2015]小园丁与老司机——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...
随机推荐
- vx小程序(1)
一.程序配置 app.json 1. pages字段——用于描述当前小程序的页面路径. 2.window字段——定义小程序所有页面的顶部背景颜色,文字颜色等. 注意:可以在pages/logs目录下的 ...
- SSTI-服务端模板注入
SSTI-服务端模板注入漏洞 原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露. ...
- 快讯:asuldb再立功,捕获史前大蛤
蛤蛤日报7月7日讯 (蛤媒体记者 申蛤 戌蛤) 昨日下午,asuldb成功于生物实验室捕获史前大蛤.据考证,史前大蛤是一种名为楠楠的生物.这种生物体型庞大,距今已有至少1e18年的寿命.这种大蛤行为古 ...
- vscode F2无法使用
rope库可能存在bug 解决方法: "python.jediEnabled": false //自动补全用微软自带
- DVWA学习记录 PartⅧ
Weak Session IDs 1. 题目 用户访问服务器的时候,为了区别多个用户,服务器都会给每一个用户分配一个 session id .用户拿到 session id 后就会保存到 cookie ...
- 数据可视化之powerBI入门(十)认识Power BI的核心概念:度量值
https://zhuanlan.zhihu.com/p/64150720 本文学习PowerBI最重要的概念:度量值 初学Power BI一般都会对度量值比较困惑,毕竟对长期接触Excel的人来说, ...
- 01-flask电商项目开发基础配置
本项目前端采用vue-cli的脚手架,后端采用Flask的Web框架.项目通过完成用户管理.权限管理.商品管理.订单管理.统计管理等功能,综合了前后端的知识,希望使大家都能受益. 1.使用到的技术如下 ...
- 为什么阿里、头条、美团这些互联网大厂都在用Spring Boot?
前言 自 2014 年发布至今,Spring Boot 的搜索指数 一路飙升.没错 Spring Boot 越来越火了,作为一名行走一线的 Java 程序员,你可能在各个方面感受到了 Spring B ...
- springboot --- 之SSM框架整合
1.pom依赖: 即:spring-boot的基本jar ---- 内置springmvc和spring Thymeleaf jar 热部署 jar ---方便二次加载 ctrl+f9再次编译 Myb ...
- ajax根据坐标查询WMS地图服务属性信息
<html lang="en"> <head> <meta charset="UTF-8"> <meta name=& ...