洛谷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;
}
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。