Kattis:Boxes
题目链接:https://open.kattis.com/problems/boxes
题目大意:给出$N$个盒子,第$i$个盒子在第$a_i$个盒子内.现在有$Q$个询问,每个询问以$M$开始,问$M$个盒子中共有几个不同的盒子(包含自身).
LCA
在线LCA算法:
树上两点$u$,$v$的最近公共祖先$A$的深度一定为$u$与$v$的简单路径中各点深度的最小值.
我们可以用$ver[2 imes N]$数组按顺序记录dfs先后进出的结点编号(因为记录进出的结点,故有$2 imes N - 1$个元素),以将树形结构拆分为线性结构;用$dep[2 imes N]$数组记录对应结点的深度;用$f[i]$记录第一次进入第$i$个结点时在$ver[2 imes N]$中的下标.
例如下图中,$ver[\,]={1,2,4,2,1,3,1}$,$dep[\,]={0,1,2,1,0,1,0}$,$f[\,]={-1,0,1,5,2}$(用$-1$表示结点$0$不存在)
于是,求$u$与$v$的简单路径中各点深度的最小值,即求$ver[\,f[u]\,]$到$ver[\,f[u]\,]$中各结点深度的最小值(这其中可能会包含$u$的子结点,但其深度必然大于$u$的深度).
而维护$ver[\,f[u]\,]$到$ver[\,f[u]\,]$中各结点深度的最小值,即维护一个线性区间的最小值,采用rmq算法,预处理复杂度为$O(nlgn)$,查询复杂度为$O(1)$.
设$A$为$x$与$y$的最近公共祖先,即$A=LCA(x,y)$.
若$A=x$,则$y$在$x$内;
若$A=y$,则$x$在$y$内;
若$A eq x$且$A eq y$,则$x$和$y$互不包含.
两两枚举最近公共祖先,舍去被包含的结点并用set去重,就可以解决这道题了.
//注意:
//可能不止有一个盒子不被其他盒子包含(因为这个问题卡了三天QAQ)
//可以添加一个虚结点下标为$0$,从而构成一颗树.
整道题的时间复杂度为$O(nlgn+q imes m^2)$
代码如下:
1 #include <iostream> 2 #include <vector> 3 #include <cmath> 4 #include <set> 5 #define pb(x) push_back(x) 6 #define N 200005 7 #define M 25 8 #define P 20 9 using namespace std; 10 vector<int>a[N]; 11 set<int>s; 12 int n,q,m,tmp,ans,qm[M]; 13 int k,ver[2*N],dep[2*N],f[N],num[N],dp[2*N][P]; 14 int dfs(int x,int d){ 15 num[x]=1; 16 f[x]=k; 17 dep[k]=d; 18 ver[k++]=x; 19 for(int i=0;i<(int)a[x].size();++i){ 20 num[x]+=dfs(a[x][i],d+1); 21 dep[k]=d; 22 ver[k++]=x; 23 } 24 return num[x]; 25 } 26 void rmq(int k){ 27 for(int i=0;i<k;++i) 28 dp[i][0]=i; 29 for(int j=1;(1<<j)<=k;++j) 30 for(int i=0;i+(1<<j)<=k;++i){ 31 int x=dp[i][j-1],y=dp[i+(1<<(j-1))][j-1]; 32 dp[i][j]=dep[x]<dep[y]?x:y; 33 } 34 } 35 int LCA(int u,int v){ 36 if(u>v)swap(u,v); 37 int len=log(v-u+1.0)/log(2.0); 38 int x=dp[u][len],y=dp[v+1-(1<<len)][len]; 39 return ver[dep[x]<dep[y]?x:y]; 40 } 41 int main(void){ 42 std::ios::sync_with_stdio(false); 43 cin>>n; 44 for(int i=1;i<=n;++i){ 45 cin>>tmp; 46 a[tmp].pb(i); 47 } 48 dfs(0,0); 49 rmq(k); 50 cin>>q; 51 while(q--){ 52 s.clear(); 53 cin>>m; 54 for(int i=0;i<m;++i) 55 cin>>qm[i]; 56 for(int i=0;i<m;++i){ 57 int tot=qm[i]; 58 for(int j=0;j<m;++j){ 59 tmp=LCA(f[tot],f[qm[j]]); 60 if(tmp==qm[j])tot=tmp; 61 } 62 s.insert(tot); 63 } 64 ans=0; 65 for(set<int>::iterator it=s.begin();it!=s.end();++it) 66 ans+=num[*it]; 67 cout<<ans<<" "; 68 } 69 }