【noi.ac #1770】B

題目

在一個 N×NN×N 的水池四周,有 4N4N 個人想要在這裏釣魚,他們圍着水池站了一圈,水池四邊每個格子都有一個人。

一個人在釣魚的時候,他需要把釣竿垂直於他所在的水池邊線放置,並且他的釣竿不能與其他人的釣竿有交叉。

每個人的釣竿長度不一定相同,但是有趣的是,處於同一邊的人的釣竿長度是有序的,從左到右可能是升序,也可能是降序。同時,相對的兩個人的魚竿長度和是不超過 NN 的。

現在,他們想要知道最多能夠允許多少人同時釣魚。

輸入格式
第一行一個整數 NN。

第二行 NN 個整數 a1,a2,⋯,ana1,a2,⋯,an,表示水池上邊的人的釣竿長度,按照從左到右的順序給出。

第三行 NN 個整數 b1,b2,⋯,bnb1,b2,⋯,bn,表示水池下邊的人的釣竿長度,按照從左到右的順序給出。

第四行 NN 個整數 c1,c2,⋯,cnc1,c2,⋯,cn,表示水池左邊的人的釣竿長度,按照從上到下的順序給出。

第五行 NN 個整數 d1,d2,⋯,dnd1,d2,⋯,dn,表示水池右邊的人的釣竿長度,按照從上到下的順序給出。

輸出格式
一個整數,表示最多能同時釣魚的人數。

樣例
Input
2
1 1
1 1
1 1
1 1
Output
4
數據範圍
10%的數據滿足:N≤5N≤5
30%的數據滿足:N≤10N≤10
60%的數據滿足:N≤200N≤200
對於100%的數據滿足:N≤10000,ai+bi≤N,ci+di≤NN≤10000,ai+bi≤N,ci+di≤N。

時間限制:1s1s
空間限制:256MB

思路

只有方向不同的可能會發生衝突,那麼一個顯然的想法就是把上下兩邊的放X集,左右兩邊的放Y集,然後如果會發生衝突就連一條邊,那麼答案就是 4N4N- 最大匹配。

但是這樣邊數太多。由於每一條邊上的人都是有序的,所以對於一個人來說,會發生衝突的一定是一段連續的區間,所以可以使用線段樹來優化連邊。具體來說就是,對於左右兩邊的分別建立線段樹,然後對線段樹的邊連 \infty 的邊。對於上下的人,便找出發生衝突的區間對應的線段樹上的節點,然後向那些節點連邊。

點數 O(N)O(N),邊數 O(NlogN)O(N\log{N})

代碼

#include<bits/stdc++.h>
#define M 2400000
#define N 85536
#define inf 0x3f3f3f3f
using namespace std;
bool cmp(int x,int y){return y<x;}
int A[N],B[N],C[N],D[N],S,T,a[N],b[M],c[M],d[M],e[N],h[N],l,m,n,q[N],r,t,x;
bool check(int*X)
{
	for(int i=1; i<n; i++)if(X[i+1]<X[i])return 0;
	return 1;
}
void add(int u,int v,int w){b[++t]=a[u],c[a[u]=t]=v,d[t]=w,b[++t]=a[v],c[a[v]=t]=u;}
void add(int u,int v,int w,int s)
{
	for(u+=m-1,v+=m+1;u^v^1;u>>=1,v>>=1)
	{
		if(~u&1)add(w,s+(u^1),inf);
		if(v&1)add(w,s+(v^1),inf);
	}
}
bool bfs()
{
	memset(h+1,-1,T<<2),l=0,r=1;
	while(l<r)for(int u=q[l++],i=a[u]; i; i=b[i])if(d[i]&&!~h[c[i]])h[q[r++]=c[i]]=h[u]+1;
	return~h[T];
}
bool dfs(int u)
{
	if(u==T)return 1;
	for(int&i=e[u]; i; i=b[i])if(d[i]&&h[c[i]]==h[u]+1&&dfs(c[i]))return d[i]--,++d[i^1];
	return 0;
}
int main()
{
	for(scanf("%d",&n),m=t=1,x=n<<2;m<n+2;m<<=1);
	T=n+n+m+m+m+m-1;
	for(int i=1; i<=n; i++)scanf("%d",A+i);
	for(int i=1; i<=n; i++)scanf("%d",B+i),B[i]=n+1-B[i];
	for(int i=1; i<=n; i++)scanf("%d",C+i);
	for(int i=1; i<=n; i++)scanf("%d",D+i),D[i]=n+1-D[i];
	for(int i=1; i<m; i++)add(n+n+i,n+i<<1,inf),add(n+n+i,n+i<<1|1,inf),add(n+n+m+m-1+i,(n+m+i<<1)-1,inf),add(n+n+m+m-1+i,n+m+i<<1,inf);
	if(check(A))
	{
		for(int i=1; i<=n; i++)
		add(lower_bound(A+1,A+C[i]+1,i)-A,C[i],i,n+n);
		for(int i=1; i<=n; i++)
		add(lower_bound(A+D[i],A+n+1,i)-A,n,n+i,n+n);
	}
	else
	{
		for(int i=1; i<=n; i++)
		add(1,upper_bound(A+1,A+C[i]+1,i,cmp)-A-1,i,n+n);
		for(int i=1; i<=n; i++)
		add(D[i],upper_bound(A+D[i],A+n+1,i,cmp)-A-1,n+i,n+n);
	}
	if(check(B))
	{
		for(int i=1; i<=n; i++)add(1,upper_bound(B+1,B+C[i]+1,i)-B-1,i,n+n+m+m-1);
		for(int i=1; i<=n; i++)add(D[i],upper_bound(B+D[i],B+n+1,i)-B-1,n+i,n+n+m+m-1);
	}
	else
	{
		for(int i=1; i<=n; i++)add(lower_bound(B+1,B+C[i]+1,i,cmp)-B,C[i],i,n+n+m+m-1);
		for(int i=1; i<=n; i++)add(lower_bound(B+D[i],B+n+1,i,cmp)-B,n,n+i,n+n+m+m-1);
	}
	for(int i=1; i<=n; i++)add(S,i,1),add(S,n+i,1),add(n+n+m+i,T,1),add(n+n+m+m-1+m+i,T,1);
	while(bfs())for(memcpy(e,a,T+1<<2);dfs(S);x--);
	printf("%d\n",x);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章