zoukankan      html  css  js  c++  java
  • [BZOJ4200][NOI2015]小园丁与老司机

    bzoj
    luogu
    loj上可以交部分分程序

    description

    小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面。田野上有 (n) 棵许愿树,编号 (1,2,3, ldots ,n),每棵树可以看作平面上的一个点,其中第 (i) 棵树 ((1 leq i leq n)) 位于坐标 ((x_i,y_i))。任意两棵树的坐标均不相同。

    老司机 Mr. P 从原点 ((0,0)) 驾车出发,进行若干轮行动。每一轮,Mr. P 首先选择任意一个满足以下条件的方向:

    1. 为左、右、上、左上 (45^{circ}) 、右上 (45^{circ}) 五个方向之一。
    2. 沿此方向前进可以到达一棵他尚未许愿过的树。

    完成选择后,Mr. P 沿该方向直线前进,必须到达该方向上距离最近的尚未许愿的树,在树下许愿并继续下一轮行动。如果没有满足条件的方向可供选择,则停止行动。他会采取最优策略,在尽可能多的树下许愿。若最优策略不唯一,可以选择任意一种。

    不幸的是,小园丁 Mr. S 发现由于田野土质松软,老司机 Mr. P 的小汽车在每轮行进过程中,都会在田野上留下一条车辙印,一条车辙印可看作以两棵树(或原点和一棵树)为端点的一条线段。

    在 Mr. P 之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像 Mr. P 一样任选一种最优策略行动。Mr. S 认为非左右方向(即上、左上(45^{circ}) 、右上 (45^{circ}) 三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有「可能留下非左右方向车辙印」的地面。

    「可能留下非左右方向车辙印」的地面应当是田野上的若干条线段,其中每条线段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:

    1. 从原点或任意一棵树出发。
    2. 只能向上、左上 (45^{circ}) 、右上 (45^{circ}) 三个方向之一移动,并且只能在树下改变方向或停止。
    3. 只能经过「可能留下非左右方向车辙印」的地面,但是同一块地面可以被多台轧路机经过。

    现在 Mr. P 和 Mr. S 分别向你提出了一个问题:

    1. 请给 Mr .P 指出任意一条最优路线。
    2. 请告诉 Mr. S 最少需要租用多少台轧路机。

    sol

    这道题又是个二合一啊。
    对于第一问,为老司机求出一条最优路径,这个我们可以(dp)得到。
    因为行走路线不能向下,我们令(y_i)相同的树为同一层,可以发现最优决策一定是:先在同一层中把所有可以走到的树全部走到,再向上走到达下一层。这么一来每一层就会有一个确定的入点和出点。(这里注意一下并不是所有层都会被经过)
    先求出每个点向下、左下、右下三个方向到达的最近的树,这是这个点作为这一层的进入点的三个前驱。设(f_i)表示到达第(i)棵树作为当前这一层的出点时最多经过多少棵树,那么就可以枚举入点,分三种情况讨论:
    1、入点就是出点,那么这一层就只能经过这一棵树。
    2、入点在出点的左侧,那么可以经过出点左侧的所有树(在这一层中先一直往左走再一直往右走直到到达出点)
    3、入点在出点的右侧,那么可以经过出点右侧的所有树。
    然后这个(dp)是可以做到(O(n))的。输出方案的时候,其实可以不记录(dp)转移的前驱,只要照着转移的方式再扫一遍所有前驱,看看是从哪个位置转移来的即可。

    接下来就需要处理小园丁的问题。我们先可以求出那些边是必须经过的边,然后问题就转化为了要求这些边的流量都至少是(1)
    这是一个上下界网络流的问题。我们先假设这些边的流量是(1),这样有的点就会不满足流量守恒。新建源点汇点对这些流量不平衡的点进行补流或者是分流。
    因为求的是最小流,按照最小流的一般思路是先从新建源点向新建汇点跑最大流,在不建立从原汇点到原源点的(inf)的情况下,这样跑出的最大流就是原图中最多的可以承担的流量,所以答案就是(sum_{d_i>0}d_i)-最大流。

    code

    码农题啊qwq

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<map>
    using namespace std;
    int gi(){
    	int x=0,w=1;char ch=getchar();
    	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    	if (ch=='-') w=0,ch=getchar();
    	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    	return w?x:-x;
    }
    #define cmax(a,b) (a<b?a=b:a)
    const int N = 5e4+5;
    const int inf = 1e9;
    struct node{
    	int x,y,id;
    	bool operator < (const node &b) const{
    		if (y==b.y) return x<b.x;return y<b.y;
    	}
    }p[N];
    int n,o[N],len,d[N],ld[N],rd[N],f[N],g[N],tag[N],ans;
    vector<int>v[N];map<int,int>M;queue<int>Q;
    struct network{
    	struct edge{int to,nxt,w;}a[N*100];
    	int S,T,head[N],cnt,tot,du[N],dep[N],cur[N];
    	void link(int u,int v){
    		for (int e=head[u];e;e=a[e].nxt) if (a[e].to==v) return;
    		a[++cnt]=(edge){v,head[u],inf};head[u]=cnt;
    		a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
    		++du[v];--du[u];tag[u]=1;
    	}
    	void link(int u,int v,int w){
    		a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
    		a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
    	}
    	bool bfs(){
    		memset(dep,0,sizeof(dep));
    		dep[S]=1;Q.push(S);
    		while (!Q.empty()){
    			int u=Q.front();Q.pop();
    			for (int e=head[u];e;e=a[e].nxt)
    				if (a[e].w&&!dep[a[e].to])
    					dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
    		}
    		return dep[T];
    	}
    	int dfs(int u,int f){
    		if (u==T) return f;
    		for (int &e=cur[u];e;e=a[e].nxt)
    			if (a[e].w&&dep[a[e].to]==dep[u]+1){
    				int tmp=dfs(a[e].to,min(a[e].w,f));
    				if (tmp) {a[e].w-=tmp;a[e^1].w+=tmp;return tmp;}
    			}
    		return 0;
    	}
    	int dinic(){
    		int res=0;
    		while (bfs()){
    			for (int i=0;i<=T;++i) cur[i]=head[i];
    			while (int tmp=dfs(S,inf)) res+=tmp;
    		}
    		return res;
    	}
    	int work(){
    		S=n+1;T=n+2;
    		for (int i=0;i<=n;++i){
    			if (du[i]>0) link(S,i,du[i]),tot+=du[i];
    			if (du[i]<0) link(i,T,-du[i]);
    		}
    		return tot-dinic();
    	}
    }G;
    bool cmp(node i,node j){return i.id<j.id;}
    int pre(int x){
    	if (~d[x]&&g[x]==f[d[x]]) return d[x];
    	if (~ld[x]&&g[x]==f[ld[x]]) return ld[x];
    	if (~rd[x]&&g[x]==f[rd[x]]) return rd[x];
    }
    void dfs(int u){
    	if (!u) return;int sz=v[p[u].y].size(),pos=0;
    	while (v[p[u].y][pos]!=u) ++pos;
    	if (f[u]==g[u]+1) {dfs(pre(u));printf("%d ",u);return;}
    	for (int i=0;i<pos;++i){
    		int x=v[p[u].y][i];
    		if (f[u]==g[x]+pos+1){
    			dfs(pre(x));
    			for (int j=i;~j;--j) printf("%d ",v[p[u].y][j]);
    			for (int j=i+1;j<=pos;++j) printf("%d ",v[p[u].y][j]);
    			return;
    		}
    	}
    	for (int i=pos+1;i<sz;++i){
    		int x=v[p[u].y][i];
    		if (f[u]==g[x]+sz-pos){
    			dfs(pre(x));
    			for (int j=i;j<sz;++j) printf("%d ",v[p[u].y][j]);
    			for (int j=i-1;j>=pos;--j) printf("%d ",v[p[u].y][j]);
    			return;
    		}
    	}
    }
    void build(int u){
    	int sz=v[p[u].y].size(),pos=0;
    	while (v[p[u].y][pos]!=u) ++pos;
    	if (f[u]==g[u]+1){
    		if (~d[u]&&g[u]==f[d[u]]) G.link(d[u],u);
    		if (~ld[u]&&g[u]==f[ld[u]]) G.link(ld[u],u);
    		if (~rd[u]&&g[u]==f[rd[u]]) G.link(rd[u],u);
    	}
    	for (int i=0;i<pos;++i){
    		int x=v[p[u].y][i];
    		if (f[u]==g[x]+pos+1){
    			if (~d[x]&&g[x]==f[d[x]]) G.link(d[x],x);
    			if (~ld[x]&&g[x]==f[ld[x]]) G.link(ld[x],x);
    			if (~rd[x]&&g[x]==f[rd[x]]) G.link(rd[x],x);
    		}
    	}
    	for (int i=pos+1;i<sz;++i){
    		int x=v[p[u].y][i];
    		if (f[u]==g[x]+sz-pos){
    			if (~d[x]&&g[x]==f[d[x]]) G.link(d[x],x);
    			if (~ld[x]&&g[x]==f[ld[x]]) G.link(ld[x],x);
    			if (~rd[x]&&g[x]==f[rd[x]]) G.link(rd[x],x);
    		}
    	}
    }
    int main(){
    	n=gi();G.cnt=1;
    	for (int i=1;i<=n;++i) p[i]=(node){gi(),gi(),i},o[i]=p[i].y;
    	sort(o+1,o+n+1);len=unique(o+1,o+n+1)-o-1;sort(p+1,p+n+1);
    	memset(d,-1,sizeof(d));M.clear();M[0]=0;
    	for (int i=1;i<=n;++i){
    		if (M.find(p[i].x)!=M.end()) d[p[i].id]=M[p[i].x];
    		M[p[i].x]=p[i].id;
    	}
    	memset(ld,-1,sizeof(ld));M.clear();M[0]=0;
    	for (int i=1;i<=n;++i){
    		if (M.find(p[i].x+p[i].y)!=M.end()) ld[p[i].id]=M[p[i].x+p[i].y];
    		M[p[i].x+p[i].y]=p[i].id;
    	}
    	memset(rd,-1,sizeof(rd));M.clear();M[0]=0;
    	for (int i=1;i<=n;++i){
    		if (M.find(p[i].y-p[i].x)!=M.end()) rd[p[i].id]=M[p[i].y-p[i].x];
    		M[p[i].y-p[i].x]=p[i].id;
    	}
    	for (int i=1;i<=n;++i){
    		p[i].y=lower_bound(o+1,o+len+1,p[i].y)-o;
    		v[p[i].y].push_back(p[i].id);
    	}
    	memset(f,-63,sizeof(f));memset(g,-63,sizeof(g));f[0]=0;
    	for (int i=1;i<=len;++i){
    		int sz=v[i].size(),mx;
    		for (int j=0;j<sz;++j){
    			int x=v[i][j];
    			if (~d[x]) cmax(g[x],f[d[x]]);
    			if (~ld[x]) cmax(g[x],f[ld[x]]);
    			if (~rd[x]) cmax(g[x],f[rd[x]]);
    			cmax(f[x],g[x]+1);
    		}
    		mx=-inf;
    		for (int j=0;j<sz;++j){
    			int x=v[i][j];
    			cmax(f[x],mx+j+1);cmax(mx,g[x]);
    		}
    		mx=-inf;
    		for (int j=sz-1;~j;--j){
    			int x=v[i][j];
    			cmax(f[x],mx+sz-j);cmax(mx,g[x]);
    		}
    	}
    	for (int i=1;i<=n;++i) cmax(ans,f[i]);
    	printf("%d
    ",ans);sort(p+1,p+n+1,cmp);
    	for (int i=1;i<=n;++i) if (f[i]==ans) {dfs(i);puts("");break;}
    	for (int i=1;i<=n;++i) if (f[i]==ans) tag[i]=1;
    	for (int i=len;i;--i)
    		for (int j=0,sz=v[i].size();j<sz;++j)
    			if (tag[v[i][j]]) build(v[i][j]);
    	printf("%d
    ",G.work());
    	return 0;
    }
    
  • 相关阅读:
    ssl双向认证
    keycloak管理用户权限
    Apollo单向SSL认证(2)
    Apollo单向SSL认证(1)
    apollo1.7.1初探(二)使用apollo订阅主题,发布主题消息
    apollo1.7.1初探(一)安装apollo、创建并启动broker
    Mosquito集群模式
    什么是MQTT协议?
    物影子操作
    kafka和mqtt的区别是什么?
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/9281144.html
Copyright © 2011-2022 走看看