P7712 [Ynoi2077] hlcpq(割点 + 优化建图)
常见优化建图技巧
先考虑如何优化题目中给出的建图方式。
首先显然只可能水平线段和竖直线段相交。
考虑对于一个水平线段,如何求出和它相交的竖直线段。
对于每个竖直线段的端点
需要维护线段树在不同
但是我们并不能直接将主席树的点作为虚点,优化建图,因为有可能一些虚点变为割点,导致原图割点减少。
考虑 P5471 [NOI2019] 弹跳 的做法,不直接建出所有边。
观察以下求割点的代码:
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;
}
发现 if 里面的部分只会运行
但是我们需要知道哪些点会进入 if,即哪些点没有被访问过。
考虑对主席树上每个点维护一个值
然后是对已经访问过的点 low[x]=min(low[x],dfn[j]),注意到 low[x]=min(low[x],low[j]),所以我们可以改为对所有点进行 low[x]=min(low[x],dfn[j])。
需要在主席树上多维护一个值
每次向下递归一定是找到一个未访问过的点,或者对经过的所有点更新
const int N=2e5+3;
int al[N],ar[N],m,rt[N],nd,id,u,v,p[N],low[N],dfn[N];
bool ans[N];
basic_string<int>ad[N],dl[N];
struct T{
int w,m,l,r;
}t[N*40];
void upd(int&k,int l,int r){//插入和删除
if(t[++nd]=t[k],k=nd,t[k].w+=v,l==r){
if(v==1)p[l]=k;
return;
}
int m=l+r>>1;
u>m?upd(t[k].r,m+1,r):upd(t[k].l,l,m);
}
void up(int k){t[k].w=t[t[k].l].w+t[t[k].r].w,t[k].m=min(t[t[k].l].m,t[t[k].r].m);}
int get(int k,int l,int r){//找点
if(!t[k].w)return 0;
if(l==r)return t[k].w=0,t[k].m=++id,l;
int m=l+r>>1,i;
if(u<=m)if(i=get(t[k].l,l,m))return up(k),i;
if(m<v)if(i=get(t[k].r,m+1,r))return up(k),i;
return up(k),0;
}
int qry(int k,int l,int r){//求最小值
if(!k||u<=l&&r<=v)return t[k].m;
int m=l+r>>1;
if(u<=m)return m<v?min(qry(t[k].l,l,m),qry(t[k].r,m+1,r)):qry(t[k].l,l,m);
return qry(t[k].r,m+1,r);
}
void tar(int x,bool b){//求割点
int c=0,i;
for(dfn[x]=low[x]=id;u=al[x],v=ar[x],i=get(rt[x],1,m);){
tar(i,0),++c,low[x]=min(low[x],low[i]);
if(low[i]==dfn[x])ans[x]=1;
}
if(u=al[x],v=ar[x],low[x]=min(low[x],qry(rt[x],1,m)),b)ans[x]=c>1;
}
int main(){
int n,i;
for(scanf("%d",&n),i=1;i<=n;++i)scanf("%d%d",al+i,ar+i),ad[al[i]+=n]+=i,dl[(ar[i]+=n)+1]+=i;
for(t[0].m=N,m=n*2;i<=m;++i)scanf("%d%d",al+i,ar+i),ad[al[i]]+=i,dl[ar[i]+1]+=i;
for(i=1;rt[i]=rt[i-1],i<=m;++i){
for(auto o:ad[i])u=o,v=1,upd(rt[i],1,m);
for(auto o:dl[i])u=o,v=-1,upd(rt[i],1,m);
}
for(i=1;i<=m;++i)if(!dfn[i])t[p[i]]={0,++id,0,0},tar(i,1);
for(i=1;i<=n;++i)cout<<ans[i];
for(puts("");i<=m;++i)cout<<ans[i];
return 0;
}