[NOI2015]小园丁与老司机,洛谷P2304,恶心的建图+有源汇上下界最小流

正题

      这题建图太恶心了,思路十分的简单,看到相同的显然可以把分层图建出来,一个流量代表一辆压路车,对于必须要走过的边就设下限流量为1,然后跑有源汇的上下界最小流就可以,但是怎么将那些边建出来呢?

      每一层考虑dp,看看同层转移到这个点的答案,如果转移点在左边,那么它可以先走到最左边,然后再走到这个点,所以对于左边的任何转移点,转移的时候加上的都是左边的点数,对于右边的转移点同理.

      把同层可能的转移点用vector存起来,把原来转移到该点值的下层转移点用vector存起来(最多三个).

      然后连边的时候从一个最大值开始走,dfs的过程中记录两个状态,当前到哪个点和到该点需要的是什么,需要的是什么有两种,第一种是以该点的答案为答案,另一种是需要该点作为中转点,转到下一层.对于前一种,我们选择同层转移点,让这个转移点作为中转点,调到下一层,对于后一种我们就需要跳到下层的转移点,并且连边.

      怎么记录路径?第一次dfs的时候,如果是第一种情况,那么这个点必选,如果中转点就是自己,那么就直接跳转,否则就找第一个中转点,按照第二段的方案来记录路径,然后再跳转,由于是第一个,所以跳的时候也会优先跳,倒着输出就可以了.

      上下界最小流用可行流的方案先跑,然后删源汇边,倒着跑一遍最大流就可以.

      说的可能要结合代码才能理解,毕竟本人也十分的菜,调了2h才过.

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值