NOI2015 小园丁和老司机
题目链接
洛谷的,有点不清楚。
建议去UOJ上或LOJ切了再搬运。
sol
老司机是一个显然的\(dp\)。
发现是个\(DAG\),可以愉快的按\(y\)坐标排序转移。
发现五个移动不好算,那么设\(g[i]\)表示从\(i\)点开始前往\(y\)坐标比\(i\)大的点的最多到的许愿树个数,\(f[i]\)表示从\(i\)进入\(y\)坐标能够最多到的许愿树个数。
先算这一层所有的\(g\),再算\(f\)。分两种情况,入点在出点右边或左边,只要从前往后\(dp\)一次,从后往前\(dp\)一次就\(ok\)了。
\(g\)的话维护三个桶,分别代表\(x+y\),\(x-y\),\(x\)的桶,用\(f\)转移\(g\)。
因为如果\(i<j,y_i==y_j\),从\(i\)点进\(j\)点出可以先往左走,再往右走到\(j\)离开,所以算的时候贡献是\(y\)坐标为\(y_i\)的点的个数减去\(j\)和它右边的点的个数,那么我们可以一开始减掉,最后算答案的时候加上\(y\)坐标为\(y_i\)的点的个数。这显然是个前缀或后缀\(max\)的优化\(dp\)。
那么我们得到了\(20\ pts\ QAQ\)。
\(40\)分的话也好做,\(dfs\)一条路出来,你怎么转移的就倒过来怎么找路径,具体可以看代码。
\(100\)分不难,按\(40\)分的思路去找所有的边,只是不\(dfs\)而是遍历每个可能出现的点。
然后就是个很裸的上下界最小流了。
听\(litble\)说直接写会\(TLE\),所以我就没写最裸的,加了个优化。
因为你肯定有解,并且\(S\)和\(T\)到每个点都有\(inf\)的边,所以我们不管它们,直接建超级源汇连原图,跑出来最大流就是图本生可以减少的流量。
减掉就\(ok\)了。
不算网络流模板这道题还挺短的\(QAQ\)。
#include<map>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#define gt getchar()
#define ll long long
#define Set(p,v) memset(p,v,sizeof(p))
#define Cpy(p,v) memcpy(p,v,sizeof(p))
inline int in()
{
int k=0,p=1;char ch=gt;
while(!isdigit(ch)&&ch!='-')ch=gt;
if(ch=='-')p=0,ch=gt;
while(isdigit(ch))k=k*10+ch-'0',ch=gt;
return p?k:-k;
}
inline int min(const int &a,const int &b){return a<b?a:b;}
inline int max(const int &a,const int &b){return a>b?a:b;}
const int inf=0x7fffffff;
class Graph
{
private:
static const int N=400005,M=1000005;
int s,t;
int dis[N];
int pre[N],pr[N],o[N],cur[N];
int head[N],nxt[M],to[M],cap[M];double w[M];
public:
int n,cnt;
inline void init(int nn,int ss,int tt){n=nn,s=ss,t=tt;cnt=-1;Set(head,-1);}
inline void add_Edge(int u,int v,int cc,int ww){to[++cnt]=v,cap[cnt]=cc,w[cnt]=ww,nxt[cnt]=head[u],head[u]=cnt;}
inline void add(int u,int v,int cc,int ww=0){add_Edge(u,v,cc,ww),add_Edge(v,u,0,-ww);}
int dfs(int u,int dist)
{
if(u==t)return dist;
for(int &i=cur[u];~i;i=nxt[i])
{
if(dis[to[i]]==dis[u]+1&&cap[i]!=0)
{
int d=dfs(to[i],min(dist,cap[i]));
if(d>0){cap[i]-=d;cap[i^1]+=d;return d;}
}
}
return 0;
}
int bfs()
{
std::queue<int>Q;Set(dis,0);
Q.push(s);dis[s]=1;
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=head[u];~i;i=nxt[i])
{
if(cap[i]>0&&dis[to[i]]==0)
{
dis[to[i]]=dis[u]+1;
Q.push(to[i]);
}
}
}
return dis[t]>0;
}
int dinic()
{
int res=0,d;
while(bfs()){Cpy(cur,head);while((d=dfs(s,inf)))res+=d;}
return res;
}
int spfa()
{
//实数流的时候务必用for给dis赋值
std::queue<int>Q;Set(dis,0x3f);Set(pr,0);Set(o,0);Set(pre,0);
dis[s]=0;Q.push(s);o[s]=1;
while(!Q.empty())
{
int u=Q.front();Q.pop();o[u]=0;
for(int i=head[u];~i;i=nxt[i])
if(cap[i]>0&&dis[to[i]]>dis[u]+w[i])
{
dis[to[i]]=dis[u]+w[i];
pr[to[i]]=i;pre[to[i]]=u;
if(!o[to[i]])o[to[i]]=1,Q.push(to[i]);
}
}
return pre[t]!=0;
}
void fyl(int &ans,int &hh)
{
ans=hh=0;
while(spfa())
{
int d=inf;
for(int i=t;i!=s;i=pre[i])d=min(d,cap[pr[i]]);hh+=dis[t]*d;ans+=d;
for(int i=t;i!=s;i=pre[i])cap[pr[i]]-=d,cap[pr[i]^1]+=d;
}
}
int fyl()
{
int hh=0;
while(spfa())
{
int d=inf;
for(int i=t;i!=s;i=pre[i])d=min(d,cap[pr[i]]);hh+=dis[t]*d;
for(int i=t;i!=s;i=pre[i])cap[pr[i]]-=d,cap[pr[i]^1]+=d;
}
return hh;
}
}cx;
typedef std::pair<int,int> P;
#define mk std::make_pair
#define fr first
#define sc second
const int N=50005;
int n,tot;P E[N*20];
struct point{int x,y,id;}a[N];
inline bool cmpx(point a,point b){return a.x==b.x?a.y<b.y:a.x<b.x;}
inline bool cmpy(point a,point b){return a.y==b.y?a.x<b.x:a.y<b.y;}
inline bool cmpi(point a,point b){return a.id<b.id;}
inline void cmax(int &x,int y,int *a,int &c,int b){if(x<y)x=y,a[(c=1)-1]=b;else if(x==y)a[c++]=b;}
inline void cmax(int &x,int y){if(x<y)x=y;}
inline void Push(int x,int y){E[++tot]=mk(x,y);}
namespace w1
{
using std::map;
map<int,int>t1,t2,t3,mp[N];
int f[N],g[N],pre[N][3],L[N],R[N],TT,o[N],cnt[N];
inline int calc(int u,int k)
{
if(u==k)return g[k];
if(u<k)return g[k]+k-L[k];
else return g[k]+R[k]-k;
}
void dfs(int val,int u)//求一组最优方案
{
if(u==-1)return;
if(u!=1)printf("%d ",a[u].id);
int l=L[u],r=R[u];
for(int i=l;i<=r;++i)
if(calc(u,i)==val)
{
if(u==i)return dfs(g[u]-1,pre[u][0]);
if(u<i)for(int j=u-1;j>=l;--j)printf("%d ",a[j].id);
else for(int j=u+1;j<=r;++j)printf("%d ",a[j].id);
if(u<i)for(int j=u+1;j<=i;++j)printf("%d ",a[j].id);
else for(int j=u-1;j>=i;--j)printf("%d ",a[j].id);
return dfs(g[i]-1,pre[i][0]);
}
}
void Dfs(int val,int u)//求所有边
{
if(u==-1||o[u])return;
int l=L[u],r=R[u];o[u]=1;
for(int i=l;i<=r;++i)
if(calc(u,i)==val)
for(int k=0;k<cnt[i];++k)
if(pre[i][k]&&pre[i][k]!=-1)
{
if(mp[i].find(k)==mp[i].end())Push(i,pre[i][k]),mp[i][k]=1;
if(!o[pre[i][k]])Dfs(g[i]-1,pre[i][k]);
}
}
void solve()
{
a[++n]=(point){0,0,0};std::sort(a+1,a+n+1,cmpy);
for(int i=n,j;i>=1;i=j-1)
{
for(j=i;j>1&&a[j-1].y==a[i].y;--j);
for(int k=j;k<=i;++k)L[k]=j,R[k]=i;
for(int k=j;k<=i;++k)
{
int x=a[k].x,y=a[k].y;g[k]=1;pre[k][cnt[k]=0]=-1;
if(t1.find(x+y)!=t1.end())cmax(g[k],f[t1[x+y]]+1,pre[k],cnt[k],t1[x+y]);
if(t2.find(x-y)!=t2.end())cmax(g[k],f[t2[x-y]]+1,pre[k],cnt[k],t2[x-y]);
if(t3.find( x )!=t3.end())cmax(g[k],f[t3[ x ]]+1,pre[k],cnt[k],t3[ x ]);
t1[x+y]=t2[x-y]=t3[x]=k;
}
for(int k=j,mx=-1e9;k<=i;++k)
cmax(f[k],g[k]),cmax(f[k],mx+i-j),cmax(mx,g[k]-k+j);
for(int k=i,mx=-1e9;k>=j;--k)
cmax(f[k],g[k]),cmax(f[k],mx+i-j),cmax(mx,g[k]-i+k);
}
printf("%d\n",f[1]-1);dfs(f[1],1);puts("");Dfs(f[1],1);
}
}
namespace w2
{
int du[N];
void solve()
{
int t=n+1,s=0,ans=0;cx.init(t+1,s,t);
for(int i=1;i<=tot;++i)
--du[E[i].fr],++du[E[i].sc],cx.add(E[i].fr,E[i].sc,inf);
for(int i=1;i<=n;++i)
if(du[i]>0)cx.add(s,i,du[i]),ans+=du[i];
else if(du[i]<0)cx.add(i,t,-du[i]);
printf("%d\n",ans-cx.dinic());
}
}
int main()
{
n=in();
for(int i=1;i<=n;++i)a[i].x=in(),a[i].y=in(),a[i].id=i;
w1::solve();w2::solve();
return 0;
}