P2304 [NOI2015] 小园丁与老司机

#include<bits/stdc++.h>
using namespace std;
 
const int N=200010,M=200010;
struct node{
	int x,y,id;
}p[N];
struct edge{
	int y,nex,c;
}s[M<<1];
int first[N],head[N],len=1,qs[N],st,ed,d[N];
int nex[N][3],f[N],ans[N],from[N],op[N],s1,t1,s2,t2,lef[N],rig[N],pos[N];//上,左上,右上
vector<int> V[N],P[N],S;
int sta[N],top=0,mmax=-1e9;
bool tf[N][2];
int n;
 
bool cmp1(const node&a,const node&b){return a.y!=b.y?a.y<b.y:a.x<b.x;}
bool cmp2(const node&a,const node&b){return a.x!=b.x?a.x<b.x:a.y<b.y;}
bool cmp3(const node&a,const node&b){return a.x+a.y!=b.x+b.y?a.x+a.y<b.x+b.y:a.x<b.x;}
bool cmp4(const node&a,const node&b){return a.y-a.x!=b.y-b.x?a.y-a.x<b.y-b.x:a.x<b.x;}
bool cmp5(const node&a,const node&b){return a.id<b.id;}
 
void ins(int x,int y,int a,int b){
	op[x]-=a;op[y]+=a;
	s[++len]=(edge){y,first[x],b-a};first[x]=len;
	s[++len]=(edge){x,first[y],0};first[y]=len;
}
 
void gins(int x,int type){
	if(tf[x][type]) return ;
	if(type==0){
		tf[x][0]=true;
		for(auto i:P[x]) ins(i,x,1,1e9),gins(i,1);
	}
	if(type==1){
		tf[x][1]=true;
		if(top!=mmax){
			sta[++top]=x;
			for(auto i:V[x]) if(i!=x){
				if(pos[i]<pos[x]){
					int now=lef[x];
					while(now!=i) sta[++top]=now,now=lef[now];
					while(lef[now]) now=lef[now];
					while(now!=i) sta[++top]=now,now=rig[now];
					sta[++top]=i;
				}
				else{
					int now=rig[x];
					while(now!=i) sta[++top]=now,now=rig[now];
					while(rig[now]) now=rig[now];
					while(now!=i) sta[++top]=now,now=lef[now];
					sta[++top]=i;
				}
				break;
			}
			else break;
		}
		for(auto i:V[x]) gins(i,0);
	}
}
 
bool bfs(int S,int T){
	for(int i=1;i<=T;i++) d[i]=0,first[i]=head[i];
	d[qs[st=ed=1]=S]=1;
	while(st<=ed){
		int x=qs[st++];
		for(int i=first[x];i;i=s[i].nex) if(s[i].c && !d[s[i].y]){
			d[s[i].y]=d[x]+1;
			qs[++ed]=s[i].y;
		}
	}
	return d[T]!=0;
}
 
int dfs(int x,int T,int t){
	if(x==T) return t;
	int tot=0;
	for(int&i=first[x];i;i=s[i].nex) if(s[i].c && d[s[i].y]==d[x]+1){
		int now=dfs(s[i].y,T,min(t-tot,s[i].c));
		s[i].c-=now;s[i^1].c+=now;tot+=now;
		if(tot==t) break;
	}
	return tot;
}
 
int Dinic(int S,int T){
	int tot=0,dx;
	while(bfs(S,T)){
		dx=dfs(S,T,1e9);
		while(dx) tot+=dx,dx=dfs(S,T,1e9);
	}
	return tot;
}
 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y),p[i].id=i;
	n++;p[n]=(node){0,0,n};
	for(int i=1;i<=n;i++) ans[i]=-1e9;ans[n]=0;
	s1=n+1;t1=s1+1;s2=t1+1;t2=s2+1;
	sort(p+1,p+1+n,cmp2);
	for(int i=1;i<n;i++) if(p[i].x==p[i+1].x) 
		nex[p[i].id][0]=p[i+1].id;
	sort(p+1,p+1+n,cmp3);
	for(int i=1;i<n;i++) if(p[i].x+p[i].y==p[i+1].x+p[i+1].y)
		nex[p[i+1].id][1]=p[i].id;
	sort(p+1,p+1+n,cmp4);
	for(int i=1;i<n;i++) if(p[i].y-p[i].x==p[i+1].y-p[i+1].x)
		nex[p[i].id][2]=p[i+1].id;
	sort(p+1,p+1+n,cmp1);
	int now=1;
	while(now<=n){
		int L=now,R=now,mmax=-1e9;S.resize(0);
		while(p[R+1].y==p[R].y) R++;
		for(int i=L;i<=R;i++){
			f[p[i].id]=ans[p[i].id],V[p[i].id].push_back(p[i].id);
			if(i!=L) lef[p[i].id]=p[i-1].id;
			if(i!=R) rig[p[i].id]=p[i+1].id;
			pos[p[i].id]=i;
		}
		for(int i=L;i<=R;i++){
			if(mmax+i-L>ans[p[i].id]) ans[p[i].id]=mmax+i-L,V[p[i].id]=S;
			else if(mmax+i-L==ans[p[i].id]) for(auto j:S) V[p[i].id].push_back(j);
			if(f[p[i].id]>mmax) mmax=f[p[i].id],S.resize(0);
			if(f[p[i].id]==mmax) S.push_back(p[i].id);
		}
		mmax=-1e9;S.resize(0);
		for(int i=R;i>=L;i--){
			if(mmax+R-i>ans[p[i].id]) ans[p[i].id]=mmax+R-i,V[p[i].id]=S;
			else if(mmax+R-i==ans[p[i].id]) for(auto j:S) V[p[i].id].push_back(j);
			if(f[p[i].id]>mmax) mmax=f[p[i].id],S.resize(0);
			if(f[p[i].id]==mmax) S.push_back(p[i].id);
		}
		for(int i=L;i<=R;i++){
			for(int j=0;j<3;j++) if(nex[p[i].id][j]){
				if(ans[nex[p[i].id][j]]<ans[p[i].id]+1) 
					P[nex[p[i].id][j]].resize(0),ans[nex[p[i].id][j]]=ans[p[i].id]+1;
				if(ans[nex[p[i].id][j]]==ans[p[i].id]+1) 
					P[nex[p[i].id][j]].push_back(p[i].id);
			}
		}
		now=R+1;
	}
	sort(p+1,p+1+n,cmp5);
	for(int i=1;i<=n;i++) mmax=max(mmax,ans[i]);
	printf("%d\n",mmax);
	for(int i=1;i<=n;i++) if(ans[i]==mmax) gins(i,1);
	for(int i=mmax;i>=1;i--) printf("%d ",sta[i]);printf("\n");
	for(int i=1;i<=n;i++) ins(s1,i,0,1e9),ins(i,t1,0,1e9);
	for(int i=1;i<=t1;i++) 
		if(op[i]>0) ins(s2,i,0,op[i]);
		else if(op[i]<0) ins(i,t2,0,-op[i]);
	ins(t1,s1,0,1e9);
	for(int i=1;i<=t2;i++) head[i]=first[i];
	Dinic(s2,t2);
	int tot=s[len].c;s[len].c=s[len^1].c=0;
	printf("%d\n",tot-Dinic(t1,s1));
}

题目描述

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

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

1.为左、右、上、左上 45°45°、右上 45°45° 五个方向之一。

2.沿此方向前进可以到达一棵他尚未许愿过的树。

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

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

在 Mr.P 之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像 Mr.P 一样任选一种最优策略行动。Mr.S 认为非左右方向(即上、左上 45°45°、右 上 45°45° 三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有“可能留下非左右方向车辙印”的地面。“可能留下非左右方向车辙印”的地面应当是田野上的若干条线段,其中每条线段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:

1.从原点或任意一棵树出发。

2.只能向上、左上 45°45°、右上 45°45° 三个方向之一移动,并且只能在树下改变方向或停止。

3.只能经过“可能留下非左右方向车辙印”的地面,但是同一块地面可以 被多台轧路机经过。

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

1.请给 Mr.P 指出任意一条最优路线。

2.请告诉 Mr.S 最少需要租用多少台轧路机。

输入格式

第 11 行包含 11 个正整数 𝑛n,表示许愿树的数量。

接下来 𝑛n 行,第 𝑖+1i+1 行包含 22 个整数 𝑥𝑖,𝑦𝑖xi​,yi​,中间用单个空格隔开,表示第 𝑖i 棵许愿树的坐标。

输出格式

包括 33 行。

第 11 行输出 11 个整数 𝑚m,表示 Mr. P 最多能在多少棵树下许愿。

输出文件的第 22 行输出 𝑚m 个整数,相邻整数之间用单个空格隔开,表示 Mr.P 应该依次在哪些树下许愿。

输出文件的第 33 行输出 11 个整数,表示 Mr. S 最少需要租用多少台轧路机。

输入输出样例

输入 #1

6
-1 1
1 1
-2 2
0 8
0 9
0 10

输出 #1

3
2 1 3
3

输入 #2

4
0 1
-2 1
2 1
3 2

输出 #2

4
1 2 3 4
2

说明/提示

样例 1 解释

最优路线共 22 条,可许愿 33 次:(0,0)→(1,1)→(−1,1)→(−2,2)(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。

至少 33 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)(−1,1)→(−2,2) 和 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。

样例 2 解释

最优路线唯一:(0,0)→(0,1)→(−2,1)→(2,1)→(3,2)(0,0)→(0,1)→(−2,1)→(2,1)→(3,2),可许愿 44 次。其中在 (0,1)(0,1) 许愿后,从 (−2,1)(−2,1) 出发沿着向右的方向能够到达的最近的未许愿过的树是 (2,1)(2,1),所以可以到达 (2,1)(2,1)。

而如果沿着 (0,0)→(0,1)→(2,1)→(−2,1)(0,0)→(0,1)→(2,1)→(−2,1) 的方向前进,此时 (−2,1)(−2,1) 右边所有树都是许愿过的,根据题目条件规定,停止前进。故无法获得最优解。

(0,0)→(0,1)(0,0)→(0,1) 与 (2,1)→(3,2)(2,1)→(3,2) 会留下非左右方向车辙印,需 22 台轧路机。

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值