P7712 [Ynoi2077] hlcpq 题解

虚式优化建图题。

首先有一个很显然的暴力,对于两条相交的线段连一条边,然后跑割点。

这个暴力的问题在于边与点的时间复杂度相差过大,无论是空间还是时间都无法承受,所以可以想到去优化建图。

首先我们假设点与边的复杂度相同,那么我们空间是承受的下的,现在想要时间承受下,由于是二维平面图,很容易想到去用扫描线。用垂直的直线去扫,扫到左端点连边,扫到右端点后不再连边,可以用队列维护。

基于这个思路,我们在这道题也用扫描线,但是队列里的元素过多,是无法承受的,乍一看队列里的元素是没有规律的,但是对于一条水平线,它进出时间是连续的,所以根据它的进出时间来判断,左端点入队,右端点出队,用线段树维护,由于每个版本都是需要用到的,显然使用主席树即可。

那么我们处理出了每个垂直线段与水平线段的相交情况,那么再横着扫一次,就处理出了每个水平线段与垂直线段的相交情况。

现在我们把所有的相交情况处理到了一棵主席树上,我们要拿它去跑割点,问题在于怎么去跑。

我们知道我们在跑割点时,维护了 \(dfn,low\) 两个值,而 \(dfn\) 是不变的,所以不需要管它,在转移 \(low\) 时,我们要判断这个点是否被遍历过,记为 \(vis\) 数组。

所以现在我们要动态维护 \(low,vis\),这东西好像直接在遍历的时候改一下值就行了……

处理好动态维护,下一个的就是遍历点了,如何得到这个点连向的下一个未被遍历的点呢。

我们首先先预处理出每个区间未使用过的点,初始时就对于每一个顶层节点 \(+1\) 然后往上 \(pushup\)。当遍历到那个节点时,将其所在的节点 \(-1\),往上 \(pushup\) 即可。

最后一步即是求连接的点中最小的 \(dfn\) 值,虽然对于由它转移出去的点是由 \(low\) 值转移的,但众所周知 \(low\le dfn\) 所以不会影响,那么这个好弄,在第一遍历到这个节点时在对应节点修改,最后维护区间最小值即可。

注意处理根节点的情况,根节点是遍历节点数大于二才为割点。

时间复杂度:\(O(n\log(n))\)

点击查看代码
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=2e5+5;
int n,L[N],R[N],m,cnt,p[N],ans[N],dfn[N],low[N],rt[N],cnp;
vector<int>a[N],b[N];
namespace tree
{
	struct node
	{
		int ls,rs,minn,sum;
	}f[40*N];
	inline void pushup(int x)
	{
		//cout<<x<<" "<<f[x].ls<<" "<<f[x].rs<<" "<<f[f[x].ls].minn<<" "<<f[f[x].rs].minn<<endl;
		f[x].sum=f[f[x].ls].sum+f[f[x].rs].sum;
		f[x].minn=min(f[f[x].ls].minn,f[f[x].rs].minn);
	}
	void update(int &x,int y,int l,int r,int nl,int k)
	{
		if(!x)x=++cnt;
		f[x]=f[y];
		
		if(l==r)
		{
			f[x].sum+=k;
			if(k==1)p[nl]=x;
			return;
		}
		int mid=(l+r)>>1;
		if(mid>=nl)update(f[x].ls=0,f[y].ls,l,mid,nl,k);
		else update(f[x].rs=0,f[y].rs,mid+1,r,nl,k);
		pushup(x);
	}
	int search(int x,int l,int r,int nl,int nr)
	{
		//cout<<x<<" "<<l<<" "<<r<<" "<<nl<<" "<<nr<<" "<<f[x].minn<<endl;//1666
		if(!x||(l>=nl&&r<=nr))return f[x].minn;
		int mid=(l+r)>>1,ans=1e9;
		if(mid>=nl)ans=min(ans,search(f[x].ls,l,mid,nl,nr));
		if(mid<nr)ans=min(ans,search(f[x].rs,mid+1,r,nl,nr));
		return ans;
	}
	int getnode(int x,int l,int r,int nl,int nr)
	{
		if(!f[x].sum)return 0;
		if(l==r)
		{
			//cout<<x<<endl;
			f[x].sum=0;
			f[x].minn=++cnp;
			return l;
		}
		int mid=(l+r)>>1;
		int num=0;
		if(mid>=nl)num=getnode(f[x].ls,l,mid,nl,nr);
		if(num)
		{
			pushup(x);
			return num;
		}
		if(mid<nr)
		{
			int num=getnode(f[x].rs,mid+1,r,nl,nr);
			pushup(x);
			return num;
		}
		pushup(x);
		return 0;
	}
}
using namespace tree;
void Tarjan(int x,bool ss)
{
	int son=0;
	dfn[x]=low[x]=cnp;
	int i=getnode(rt[x],1,m,L[x],R[x]);
	//cout<<endl;
	for(;i;i=getnode(rt[x],1,m,L[x],R[x]))
	{
		//cout<<endl;
		son++;
		Tarjan(i,1);
		low[x]=min(low[x],low[i]);
		//cout<<x<<" "<<i<<" "<<L[i]<<" "<<R[i]<<" "<<dfn[x]<<" "<<low[i]<<endl;
		if(dfn[x]==low[i])ans[x]=1; 
	}
	low[x]=min(low[x],search(rt[x],1,m,L[x],R[x]));
	if(!ss)ans[x]=son>1;
}
int main()
{
//	freopen("segment4.in","r",stdin);
//	freopen("data.out","w",stdout);
	scanf("%d",&n);
	m=2*n;
	for(int i=1;i<=n;i++)scanf("%d%d",&L[i],&R[i]),L[i]+=n,R[i]+=n;
	for(int i=1;i<=n;i++)scanf("%d%d",&L[i+n],&R[i+n]);
	for(int i=1;i<=m;i++)a[L[i]].push_back(i),b[R[i]+1].push_back(i);
	f[0].minn=1e9;
	for(int i=1;i<=m;i++)
	{
		rt[i]=++cnt;
		f[rt[i]]=f[rt[i-1]];
		int len=a[i].size();
		for(int j=0;j<len;j++)update(rt[i],rt[i],1,m,a[i][j],1);
		len=b[i].size();
		for(int j=0;j<len;j++)update(rt[i],rt[i],1,m,b[i][j],-1);
	}
//	cout<<cnt<<endl;
	//cout<<f[82].ls<<" "<<f[82].rs<<endl;
	for(int i=1;i<=m;i++)if(!dfn[i])f[p[i]].sum=0,f[p[i]].minn=++cnp,Tarjan(i,0);
	//for(int i=1;i<=m;i++)cout<<i<<" "<<dfn[i]<<" "<<low[i]<<endl;
	for(int i=1;i<=n;i++)printf("%d",ans[i]);
	printf("\n");
	for(int i=1;i<=n;i++)printf("%d",ans[i+n]);
	printf("\n");
	return 0;
}
posted @ 2023-03-09 21:39  Gmt丶Fu9ture  阅读(66)  评论(0)    收藏  举报