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;
}