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 ...
随机推荐
- 与跨域相关的 jsonp 劫持与 CORS 配置错误
参考文章: CORS(跨域资源共享)错误配置漏洞的高级利用 JSONP劫持CORS跨源资源共享漏洞 JSONP绕过CSRF防护token 读取型CSRF-需要交互的内容劫持 跨域资源共享 CORS 详 ...
- 利用CSS变量实现炫酷的悬浮效果
最近,我从 Grover网站 上发现以一个好玩儿的悬停动画,这个动画是将鼠标移动到订阅按钮上移动光标,会跟随光标实现相应的彩色渐变. 这个想法很简单,但是它能使这个按钮脱颖而出,人们一下子就注意到它了 ...
- BZOJ 2200 道路与航线(图论)
BZOJ 2200 道路与航线 题目大意 有两种路,一种没负数,一种没环,求单源最短路. solution 存在负边权Dij一定不能用嘛,显然不是 根据题意能看出来是tarjan,将双向边缩点,得到的 ...
- VS2019阅读源码 翻译注释插件
VS翻译插件: Comment Translator China https://marketplace.visualstudio.com/items?itemName=netcorevip.Comm ...
- REST,RPC和GraphQL应用场景,WebHooks、WebSocket、HTTP Streaming应用场景。
一.请求--响应API. 请求--响应类的API的典型做法是,通过基于HTTP的Web服务器暴露一个/套接口.API定义一些端点,客户端发送数据的请求到这些端点,Web服务器处理这些请求,然后返回响应 ...
- ASP.NET网页请求以及处理全过程(反编译工具查看源代码)
本文是自己查看源码后的个人总结,不保证其准确性.大家可作为参考. 浏览器和服务器之间的通信. 当敲一个域名到浏览器上面,然后回车的时候,如:http://www.baidu.com/index.asp ...
- (四)pandas的拼接操作
pandas的拼接操作 #重点 pandas的拼接分为两种: 级联:pd.concat, pd.append 合并:pd.merge, pd.join 0. 回顾numpy的级联 import num ...
- CentOS7安装Oracle 11g
准备工作 1.下载Oracle安装包:linux.x64_11gR2_database_1of2.zip 和 linux.x64_11gR2_database_2of2.zip ,可以下载到本地,通过 ...
- 数据可视化之分析篇(一)使用Power BI进行动态帕累托分析
https://zhuanlan.zhihu.com/p/57763423 通过简单的点击交互,就能进行动态分析发现见解,才是我们需要的,恰好这也是 PowerBI 所擅长的. 就帕累托分析来说,能从 ...
- python技巧 namedtuple
python的namedtuple可以创建一个带字段名的元祖和一个带名字的类 In [1]: from collections import namedtuple ...: ...: nginx=na ...