洛谷P7712 [Ynoi2077] hlcpq 题解

题目链接:P7712 [Ynoi2077] hlcpq

题目大意:给定平面上 \(2n\) 条线段,\(n\)\(n\) 纵,对每条线段建点,有交即连边,保证不存在在同一直线上的两线段,询问每一个点是否是割点,\(n\leq 10^5\)


题解:我们可以非常容易地通过扫描线+主席树来优化建图。

然而我们会发现,这玩意儿和最短路不太一样,割点是会随着图的形态的变化而变化,所以这么一搞就挂掉了。

所以我们可以容易想到观察割点代码:

void tar(int x){
	int c=0,i=he[x],j;
	for(dfn[x]=low[x]=++id;i;i=ne[i])if(!dfn[j=to[i]]){
		tar(j),++c,low[x]=min(low[x],low[j]);
		if(low[j]==dfn[x])f[x]=1;
	}else low[x]=min(low[x],dfn[j]);
	if(x==r)f[x]=c>1;
}

发现我们需要对所有可到达点的 dfn\(\min\),因为 \(low_u\leq dfn_u\)

所以我们似乎可以用线段树来维护这个东西,然后查询第一个 \(0\) 的位置也可以用线段树来维护。

但是优化建图貌似是主席树才能做?

所以我们直接在主席树上修改,因为每一个点被查询的时候一定被更新过了,所以没有问题,时间复杂度 \(O(n\log n)\)

代码:

#include <cstdio>
#include <vector>
const int Inf=0x3f3f3f3f;
const int Maxn=100000;
int n;
int dfn_tot;
namespace Tree{
	struct Segment_Node{
		int lson,rson;
		int sum,mn_val;
	}seg[Maxn*80+5];
	int id_tot;
	void push_up(int root){
		seg[root].sum=seg[seg[root].lson].sum+seg[seg[root].rson].sum;
		seg[root].mn_val=std::min(seg[seg[root].lson].mn_val,seg[seg[root].rson].mn_val);
	}
	int insert(int &root,int x,int a,int v,int left=1,int right=n){
		int last=root;
		root=++id_tot;
		seg[root]=seg[last];
		if(left==right){
			seg[root].sum=a,seg[root].mn_val=v;
			return root;
		}
		int mid=(left+right)>>1;
		int ans;
		if(x<=mid){
			ans=insert(seg[root].lson,x,a,v,left,mid);
		}
		else{
			ans=insert(seg[root].rson,x,a,v,mid+1,right);
		}
		push_up(root);
		return ans;
	}
	int query_min(int root,int l,int r,int left=1,int right=n){
		if(l>right||r<left||root==0){
			return Inf;
		}
		if(l<=left&&r>=right){
			return seg[root].mn_val;
		}
		int mid=(left+right)>>1;
		return std::min(query_min(seg[root].lson,l,r,left,mid),query_min(seg[root].rson,l,r,mid+1,right));
	}
	int query_fir(int root,int l,int r,int left=1,int right=n){
		if(l>right||r<left||root==0){
			return -1;
		}
		if(seg[root].sum==0){
			return -1;
		}
		if(left==right){
			seg[root].sum=0,seg[root].mn_val=++dfn_tot;
			return left;
		}
		int mid=(left+right)>>1;
		int ans=query_fir(seg[root].lson,l,r,left,mid);
		if(ans==-1){
			ans=query_fir(seg[root].rson,l,r,mid+1,right);
		}
		push_up(root);
		return ans;
	}
}
int Root[2][Maxn+5];
struct Segment{
	int l,r;
}seg[2][Maxn+5];
std::vector<int> add_lis[2][Maxn+5],del_lis[2][Maxn+5];
int dfn[2][Maxn+5],low[2][Maxn+5];
int pos[2][Maxn+5];
char ans[2][Maxn+5];
void tarjan(int t,int u,bool s=0){
	int num_son=0;
	for(int v=Tree::query_fir(Root[t^1][u],seg[t][u].l,seg[t][u].r);v!=-1;v=Tree::query_fir(Root[t^1][u],seg[t][u].l,seg[t][u].r)){
		dfn[t^1][v]=low[t^1][v]=dfn_tot;
		tarjan(t^1,v);
		num_son++;
		low[t][u]=std::min(low[t][u],low[t^1][v]);
		if(low[t^1][v]==dfn[t][u]){
			ans[t][u]='1';
		}
	}
	low[t][u]=std::min(low[t][u],Tree::query_min(Root[t^1][u],seg[t][u].l,seg[t][u].r));
	if(s){
		ans[t][u]='0'+(num_son>1);
	}
}
int main(){
	Tree::seg[0].sum=0,Tree::seg[0].mn_val=Inf;
	scanf("%d",&n);
	for(int t=0;t<2;t++){
		for(int i=1;i<=n;i++){
			scanf("%d%d",&seg[t][i].l,&seg[t][i].r);
			add_lis[t][seg[t][i].l].push_back(i),del_lis[t][seg[t][i].r+1].push_back(i);
		}
	}
	for(int t=0;t<2;t++){
		for(int i=1;i<=n;i++){
			Root[t][i]=Root[t][i-1];
			for(int j:del_lis[t][i]){
				Tree::insert(Root[t][i],j,0,Inf);
			}
			for(int j:add_lis[t][i]){
				pos[t][j]=Tree::insert(Root[t][i],j,1,Inf);
			}
		}
	}
	for(int i=1;i<=n;i++){
		ans[0][i]=ans[1][i]='0';
	}
	for(int t=0;t<2;t++){
		for(int i=1;i<=n;i++){
			if(dfn[t][i]==0){
				dfn[t][i]=low[t][i]=++dfn_tot;
				Tree::seg[pos[t][i]].sum=0,Tree::seg[pos[t][i]].mn_val=dfn[t][i];
				tarjan(t,i,1);
			}
		}
	}
	puts(ans[0]+1);
	puts(ans[1]+1);
	return 0;
}
posted @ 2022-02-05 21:29  with_hope  阅读(121)  评论(0编辑  收藏  举报