题目描述
小林要搬家啦!
小林把家搬到了一个长长的屋子里,屋子里一共有N个房间连成一排,从左到右依次标号为1…N。每两个相邻的房间之间都有一扇门,门上有一把锁,第i个房间和第i+1个房间之间的锁类型为Ci。为了防止康娜胡闹,小林把钥匙都藏在了房间里,第i个房间里放了Bi把钥匙,钥匙的种类分别为Ai[1],Ai[2],…,Ai[Bi]。注意,每把钥匙只能打开对应类型的锁,同一种类型可能存在多把钥匙和多把锁,钥匙开锁后不会消失。
但是这样仍然不能阻止康娜胡闹,因此,小林会在康娜睡着的时候把康娜移动到别的房间里去。在接下来的Q天里,第i天,康娜醒来的时候会发现自己身处第Xi个房间里,而她想要到第Yi个房间里去。
康娜不知道自己能不能到达第Yi个房间,请你帮助她计算出,她能否在第Xi个房间出发,拾取到达房间的所有钥匙并打开对应的锁,最终到达第Yi个房间。
记忆化搜索
设A[i]表示从i出发能到达的区间。
如果i能到达j,有A[i]包含A[j]。
如果i和j能互达,有A[i]=A[j]。
预处理开每扇门所需要钥匙在这扇门往左往右的第一个房间。
这样可以快速判断已有一个区间的钥匙能不能开某扇门。
接下来记忆化搜索。我们用并查集表示A相同的点。
假如在搜索j,拓展到k。
若k已经搜过了,A[j]=A[j] or A[k]。
若k已经搜到了,我们用并查集缩j和k并退出当前搜索。
若k未搜过,我们搜索k。
这样的复杂度是对的,因为任意一扇门只会开一次。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=500000+10;
int left[maxn],right[maxn],last[maxn],c[maxn];
int h[maxn],go[maxn],next[maxn];
int fa[maxn],sta[maxn],L[maxn],R[maxn];
bool bz[maxn],pd[maxn];
int i,j,k,l,r,t,n,m,tot,top,cnt,x,y;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
void merge(int x,int y){
int a=getfa(x),b=getfa(y);
if (a==b) return;
L[b]=min(L[b],L[a]);
R[b]=max(R[b],R[a]);
fa[a]=b;
}
int main(){
freopen("long.in","r",stdin);freopen("long.out","w",stdout);
n=read();
fo(i,1,n-1) c[i]=read();
fo(i,1,n){
k=read();
fo(j,1,k){
t=read();
add(i,t);
}
L[i]=R[i]=i;
}
fo(i,1,n) last[i]=n+1;
fd(i,n,1){
if (i<n) right[i]=last[c[i]];
t=h[i];
while (t){
last[go[t]]=i;
t=next[t];
}
}
fo(i,1,n) last[i]=0;
fo(i,1,n){
if (i>1) left[i]=last[c[i-1]];
t=h[i];
while (t){
last[go[t]]=i;
t=next[t];
}
}
fo(i,1,n)
if (!bz[getfa(i)]){
x=getfa(i);
sta[top=1]=x;
pd[x]=1;
while (top){
x=getfa(sta[top]);
if (L[x]>1&&right[L[x]-1]<=R[x]) y=getfa(L[x]-1);
else if (R[x]<n&&left[R[x]+1]>=L[x]) y=getfa(R[x]+1);
else{
pd[x]=0;
bz[x]=1;
top--;
continue;
}
if (bz[y]){
L[x]=min(L[x],L[y]);
R[x]=max(R[x],R[y]);
}
else if (pd[y]){
merge(y,x);
top--;
}
else{
sta[++top]=y;
pd[y]=1;
}
}
}
m=read();
while (m--){
j=read();
k=read();
j=getfa(j);
if (L[j]<=k&&k<=R[j]) printf("YES\n");else printf("NO\n");
}
}