Description
Solution
- 首先注意到如果
x
−
>
y
,
y
−
>
z
x->y,y->z
x−>y,y−>z,那么
x
−
>
z
x->z
x−>z。
- 因此考虑
x
x
x能够到
y
y
y,如果
y
y
y不能够到
x
x
x,那么
p
x
>
p
y
p_x>p_y
px>py,
x
x
x就失去了意义,同理所有能够到达
x
x
x的点也失去了意义。
- 因此可以考虑维护一个栈,每一个栈中的元素是一个强连通分量,并且它们形成了一条链。每一次从栈顶进行扩展,如果能够到一个无意义的点,那么栈中所有元素都无意义。如果到了一个栈中的点,就缩一下强连通分量。如果到了一个未到的点,就考虑这个点能否回来,因此将其将入栈中即可。最终在有意义的强连通分量中找即可。
- 合并需要用线段树维护一个钥匙集合,叶子挂一个链表表示连的边,外面用并查集就可以做到
(
n
+
m
)
l
o
g
n
(n+m)log\ n
(n+m)log n.
#include "keys.h"
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 300005
#define maxm 12000005
using namespace std;
int n,i,j,k,c[maxn];
int em,e[maxn*2],nx[maxn*2];
int tot,t[maxm],tc[maxm];
int ls[maxm],tl[maxm],tr[maxm],rt[maxn],sz[maxm];
void change(int &x,int l,int r,int p){
if (!x) x=++tot;
if (l==r) {tc[x]=1,t[x]=tc[x]&(ls[x]>0); return;}
int mid=(l+r)>>1;
if (p<=mid) change(tl[x],l,mid,p); else change(tr[x],mid+1,r,p);
t[x]=t[tl[x]]|t[tr[x]];
}
void add(int &x,int l,int r,int p,int y){
if (!x) x=++tot;
if (l==r) {em++,e[em]=y,nx[em]=ls[x],ls[x]=em,sz[x]++;return;}
int mid=(l+r)>>1;
if (p<=mid) add(tl[x],l,mid,p,y); else add(tr[x],mid+1,r,p,y);
}
void insert(int x,int y,int z){add(rt[x],1,n,z,y),add(rt[y],1,n,z,x);}
int fa[maxn],g[maxn],vis[maxn],in[maxn],d[maxn],bz[maxn];
int father(int x){return (fa[x]==x)?x:fa[x]=father(fa[x]);}
void merge(int x,int y,int l,int r){
if (l==r){
if (sz[x]<sz[y]) swap(ls[x],ls[y]),swap(sz[x],sz[y]);
sz[x]+=sz[y];tc[x]|=tc[y],t[x]=tc[x]&(sz[x]>0);
for(int i=ls[y];i;){int k=nx[i];nx[i]=ls[x],ls[x]=i;i=k;}
return;
}
int mid=(l+r)>>1;
if (!tl[x]) tl[x]=tl[y]; else
if (tl[y]) merge(tl[x],tl[y],l,mid);
if (!tr[x]) tr[x]=tr[y]; else
if (tr[y]) merge(tr[x],tr[y],mid+1,r);
t[x]=t[tl[x]]|t[tr[x]];
}
int find(int x,int l,int r){
if (!t[x]) return 0;
if (l==r) {
int k=ls[x];
sz[x]--,ls[x]=nx[k],t[x]=tc[x]&(ls[x]>0); return e[k];
}
int mid=(l+r)>>1,k;
if (t[tl[x]]) k=find(tl[x],l,mid); else k=find(tr[x],mid+1,r);
t[x]=t[tl[x]]|t[tr[x]]; return k;
}
int Findone(int x){
int y=find(rt[x],1,n);
while (y&&father(y)==father(x))
y=find(rt[x],1,n);
return y;
}
void Merge(int x,int y){
g[y]+=g[x],fa[x]=y;
if (!rt[y]) rt[y]=rt[x];
else if (rt[x]) merge(rt[y],rt[x],1,n);
}
vector<int> find_reachable(vector<int> r,vector<int> u,vector<int> v,vector<int> w) {
n=r.size();
for(i=1;i<=n;i++) c[i]=r[i-1]+1;
for(i=0;i<u.size();i++) insert(u[i]+1,v[i]+1,w[i]+1);
for(i=1;i<=n;i++)
change(rt[i],1,n,c[i]);
for(i=1;i<=n;i++) fa[i]=i,g[i]=1;
for(int S=1;S<=n;S++) if (!vis[S]){
d[d[0]=1]=S,in[S]=vis[S]=1; int tp=0;
while (1){
int x=father(d[d[0]]),y=father(Findone(x));
if (!y) break;
if (vis[y]){
if (in[y]){
while (d[d[0]]!=y) {
Merge(d[d[0]],d[d[0]-1]);
in[d[d[0]]]=0,d[0]--;
}
} else {tp=1;break;}
} else d[++d[0]]=y,vis[y]=in[y]=1;
}
for(i=1;i<=d[0]-1+tp;i++) bz[d[i]]=1;
while (d[0]) in[d[d[0]--]]=0;
}
int mi=n;
for(i=1;i<=n;i++) if (father(i)==i&&!bz[i]) mi=min(mi,g[i]);
vector<int> ans;
for(i=1;i<=n;i++) ans.push_back(g[father(i)]==mi&&!bz[father(i)]);
return ans;
}