QOJ 5403 树数术
我们认为自己是自己的祖先,并且下文说的部分内容是在 \(a\) 数组上进行的,例如 \(i\) 可能指的是 \(a_i\),\(j\) 指的是 \(a_j\) 等等。
我们先考虑一条链怎么做。我们预处理 \(f_{0,i}\) 表示 \(i\) 后面的第一个 \(j\),使得 \(j\) 满足它是 \(i\sim j\) 之间所有点的祖先。
这个显然是好处理的,直接单调栈即可。然后对于每一个读入的区间,倍增找出第一个是其前面所有区间中的点的 lca 的点,然后从这个点开始跳 \(f\) 数组。
现在考虑链变为树怎么做。发现 dfn 的限制变为了(设 \(u\) 子树中 dfn 的最小值为 \(l_u\),最大值为 \(r_u\))\(l_j\le L,r_j\ge R\),其中 \(L,R\) 是 \(i\sim j\) 中所有 \(l\) 的最小值和 \(r\) 的最大值。
显然我们可以先只满足第一个限制,求出一个 \(f\) 数组,仍然使用单调栈即可。
接下来考虑怎么在满足第一个限制的同时满足第二个限制。本段的下一个,指的是下一个对于该点满足第一个限制的点。我们设 \(v_{0,i}\) 表示下一个满足第二个限制的点的 \(r\) 是多少,没有则为 \(0\)。
考虑怎么把左子区间和右子区间合并到当前区间(即倍增数组 \(v\) 的合并)。首先 \(v\) 继承左子区间的 \(v\),然后看右子区间的 \(v\) 是否不小于左子区间中 \(r\) 的最大值,如果是,那么即这个东西同时是右子区间和左子区间的 \(r\) 的最大值,也就是说可以继承。
那么继承完之后,我们就会求 \(v\) 了。接下来我们考虑怎么处理每个点下一个合法的点(即对于每个 \(i\) 找出最小的 \(j\),使得 \(j\) 是 \(i\sim j\) 中所有点的祖先),只需要倍增地找,看 \(v_{j,cur}\) 是否不小于这一段区间中最大的 \(r\),如果不是,则向后跳 \(f\),否则就找到了我们要找的 \(j\)。
我们设这个数组是 \(ne\) 数组,然后倍增一下,就可以处理询问了。
对于当前区间,我们先倍增找出第一个点 \(cur\),使得它是前面所有点的祖先(具体实现见代码,因为比较好理解)。然后一直从这个点向后跳 \(ne\),直到跳出这个区间,一共跳了多少点就贡献了多少。
对于最小最大值问题,使用 ST 表即可。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 700005
#define K 20
#define mod 1000000007
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
int T=1,n,m,q,cnt,a[N],l[N],r[N];
int f[K][N],v[K][N],ne[K][N];
vector<int>e[N];
void add(int a,int b){
e[a].push_back(b);
}
void dfs(int u,int fa){
l[u]=++cnt;
for(auto j:e[u]){
if(j==fa)continue;
dfs(j,u);
}
r[u]=cnt;
}
struct st{
int lg[N];
int f[K][N],g[K][N];
void init(){
for(int i=1;i<=m;i++){
if(i!=1)lg[i]=lg[i>>1]+1;
f[0][i]=r[a[i]];
g[0][i]=l[a[i]];
}
for(int j=1;j<K;j++){
for(int i=1;i+(1<<j-1)<=m;i++){
f[j][i]=max(f[j-1][i],f[j-1][i+(1<<j-1)]);
g[j][i]=min(g[j-1][i],g[j-1][i+(1<<j-1)]);
}
}
}
int qry_r(int l,int r){
if(l>r)return -1;
int k=lg[r-l+1];
return max(f[k][l],f[k][r-(1<<k)+1]);
}
int qry_l(int l,int r){
if(l>r)return m+1;
int k=lg[r-l+1];
return min(g[k][l],g[k][r-(1<<k)+1]);
}
}st;
void solve(int cs){
cin>>n>>m>>q;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);add(b,a);
}
dfs(1,0);
for(int i=1;i<=m;i++){
cin>>a[i];
}
st.init();
stack<int>stk;
for(int i=m;i;i--){
while(!stk.empty()&&l[a[stk.top()]]>l[a[i]])stk.pop();
if(!stk.empty())f[0][i]=stk.top();
stk.push(i);
}
for(int i=1;i<=m;i++){
if(r[a[f[0][i]]]>=st.qry_r(i,f[0][i])){
v[0][i]=r[a[f[0][i]]];
}
}
for(int i=m;i;i--){
for(int j=1;j<K;j++){
f[j][i]=f[j-1][f[j-1][i]];
v[j][i]=v[j-1][i];
if(f[j][i]&&v[j-1][f[j-1][i]]>=st.qry_r(i,f[j-1][i])){
v[j][i]=v[j-1][f[j-1][i]];
}
}
}
for(int i=m;i;i--){
int p=i;
for(int j=K-1;~j;j--){
if(f[j][p]&&v[j][p]<st.qry_r(i,p)){
p=f[j][p];
}
}
ne[0][i]=f[0][p];
}
for(int i=m;i;i--){
for(int j=1;j<K;j++){
ne[j][i]=ne[j-1][ne[j-1][i]];
}
}
while(q--){
int k;
int L=m,R=1;
int res=0;
cin>>k;
while(k--){
int x,y;
cin>>x>>y;
int cur=x;
for(int j=K-1;~j;j--){
if(ne[j][cur]&&!(l[a[ne[j][cur]]]<=L&&r[a[ne[j][cur]]]>=R)){
cur=ne[j][cur];
}
}
if(!(l[a[cur]]<=L&&r[a[cur]]>=R))cur=ne[0][cur];
L=min(L,st.qry_l(x,y));
R=max(R,st.qry_r(x,y));
if(cur>y||!cur)continue;
res++;
for(int j=K-1;~j;j--){
if(ne[j][cur]&&ne[j][cur]<=y){
cur=ne[j][cur];
res+=1<<j;
}
}
}
cout<<res<<'\n';
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}

浙公网安备 33010602011771号