牛半仙的妹子Tree题解

· · 题解

题面

做法

先补一下ST表求LCA的做法。

欧拉序就是dfs遍历这颗树经过每个节点的顺序。

欧拉序有一个优美的性质,就是给定两个端点 l,r ,则 [l,r] 之间深度最小值即是 l,r 的LCA。

我们 dfs 时需要统计 dep_u,dfn_u 以及欧拉序

如图,其欧拉序为1 5 6 5 1 2 4 2 3 2 1

比如6与4之间的最小值为1,显然该点就是这两点的LCA。

由于一个有儿子的点可能被经过不止一次,所以序列长度比 n 大很正常。

我们还需要一个dfn数组来统计每个节点第一次被经过的时间,当然这个时间戳依然可能比 n 大,所代表的就是其第一次在欧拉序中出现的位置。

对于上图,其dfn1 6 9 7 2 3,两者的联系就在于dfn_i 代表的是对于节点 i 第一次出现在欧拉数组中的下标是 dfn_i

比如 dfn_2 为6,而其在欧拉序中首次出现的位置也是6。

欧拉序找LCA就是对于一个欧拉序数组,一组查询为 u,v,我们应当在 [dfn_u,dfn_v] 间找深度最小值,ST表显然可以维护。

在建表时,由于欧拉序的性质,应当选择深度较小的,而不是简单的取min(st[i][j-1],st[i+(1<<(j-1))][j-1])

在询问时,对于两个点 u,v ,先利用 dfn_u,dfn_v 找到在欧拉序中第一次出现的位置,然后再取深度较小点作为LCA。

再回到题目,不妨朴素的想,考虑每个点是否被染色,就要遍历上一次清零后的所有染色操作,设染色操作染了 x,询问点是 ydis_{x,y} 表示两点距离, TIME_i 表示对于 i 点操作的时间,如果 dis_{x,y} \le TIME_{y}-TIME_{x} 那显然已经扩展到了。

考虑分块,由于操作是按时间顺序排序的,我们对于操作分块。

我们设块长为 len ,对于每个块内的染色操作,我们直接暴力判断,即在一个块内从上一次清零时刻+1开始(可能本块没有,直接从左端点开始)。

还有一种情况,就是上一次清零在块外面(上面的情况已经保证统计到被块内染色的情况),如果我们能够知道该点是否被块外的染色操作染色,即可 O(1) 判断。

假设该块之前的都已经算完了,我们现在枚举染色时间,判断该块中的染色操作是否会更新要染的这个点的染色时间,因为如果当前染色操作的时间比该点被染色的时间还要晚,那显然不用更新,如果可以,将当前块的所有染色操作加入队列并更新被染点的时间。进行Bfs,如果染色操作会使某个点最早被染色时间更早就修改,否则不修改,枚举完时间后将队列中剩下的点继续扩展直到遍历整棵树。

这样复杂度是 O(n) 的。

块长为 len 块数为 m/len ,时间复杂度为 O(m/len \times (n + len^2))len\sqrt{n}n,m 同阶即为 O(m \times \sqrt{n})

code
#include<bits/stdc++.h>
using namespace std;
#define syt cerr<<"sytakioi"<<endl;
const int maxn=2e5+10,inf=0x3f3f3f3f;
struct node {
    int nxt,to;
} edge[maxn<<1];
struct OP {
    int op,x;
} op[maxn<<1];
int head[maxn<<1],cnt=0;
void add(int u,int v) {
    cnt++;
    edge[cnt].to=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}
int st[maxn<<1][30],fa[maxn<<1];
int tim[maxn],l[maxn],r[maxn];
int n,m,num=0;
int dep[maxn<<1],rl[maxn<<1],dfn[maxn<<1];
void dfs(int u,int fath) {
    rl[++num]=u;
    dep[u]=dep[fath]+1;
    dfn[u]=num;
    fa[u]=fath;
    for(int i=head[u]; i; i=edge[i].nxt) {
        int v=edge[i].to;
        if(v==fath) continue ;
        dfs(v,u);
        rl[++num]=u;
    }
}
int lca(int x,int y) {
    int u=dfn[x],v=dfn[y];
    if(u>v)
        swap(u,v);
    int len=log2(v-u+1);
    int uu=st[u][len],vv=st[v-(1<<len)+1][len];
    if(dep[uu]<dep[vv])
        return uu;
    else
        return vv;
}
int dis(int u,int v) {
    return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int s;
int main() {
    cin>>n>>m;
    for(int i=1; i<n; i++) {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    dfs(1,0);
//  for(int i=1; i<=n; i++)
//      cout<<dfn[i]<<" ";
//  cout<<endl;
//  for(int i=1; i<=num; i++)
//      cout<<rl[i]<<" ";
    memset(tim,inf,sizeof(tim));
    for(int i=1; i<=num; i++)
        st[i][0]=rl[i];
    for(int j=1; j<=log2(num); j++) {
        for(int i=1; i+(1<<j)-1<=num; i++) {
            int u=st[i][j-1],v=st[i+(1<<(j-1))][j-1];
            if(dep[u]<dep[v])
                st[i][j]=u;
            else
                st[i][j]=v;
        }
    }
    for(int i=1; i<=m; i++) {
        cin>>op[i].op>>op[i].x;
    }
    int len=sqrt(n);
    int blocks=m/len;
    if(blocks*len<m)blocks++;
    for(int i=1; i<=blocks; i++) {
        l[i]=len*(i-1)+1,r[i]=len*i;
    }
    r[blocks]=m;
    int gc=0;
    queue<int> q;
    //for(int i=1; i<=blocks; i++)
    //  cout<<" bl: "<<l[i]<<" "<<r[i]<<endl;
    for(int i=1; i<=blocks; i++) {
        //syt
//      for (int j = 1; j <= n; j++) {
//          cout<<tim[j]<<" ";
//      }
//      cout<<endl;
        for(int j=l[i]; j<=r[i]; j++) {
            //syt
            if(op[j].op==1) continue;
            else if(op[j].op==2) gc=j;
            else {
                bool flag=0;
                for(int k=max(gc+1,l[i]); k<=j-1; k++)

                    if(op[k].op==1 && j-k>=dis(op[k].x,op[j].x)) {
                        flag=1;
                        break;
                    }
                //cout<<flag;
                if(gc<l[i] && tim[op[j].x]<=j) flag=1;
                //syt
                if(flag==1) {
                    cout<<"wrxcsd"<<endl;

                } else
                    cout<<"orzFsYo"<<endl;

            }
        }
        while(!q.empty())
            q.pop();
        if(gc>=l[i])
            for(int j=1; j<=n; j++)
                tim[j]=inf;
        for(int j=max(gc+1,l[i]); j<=r[i]; j++) {
            if(op[j].op==1 && j<tim[op[j].x])
                tim[op[j].x]=j,q.push(op[j].x);
            while(!q.empty()) {
                int u=q.front();
                if(tim[u]>j)break;
                q.pop();
                for(int I=head[u]; I; I=edge[I].nxt) {
                    int v=edge[I].to;
                    if(tim[v]>tim[u]+1) {
                        tim[v]=tim[u]+1;
                        q.push(v);
                    }
                }
                if(tim[fa[u]]>tim[u]+1 && fa[u]) {
                    tim[fa[u]]=tim[u]+1;
                    q.push(fa[u]);
                }
            }
        }
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int I=head[u]; I; I=edge[I].nxt) {
                int v=edge[I].to;
                if(tim[v]>tim[u]+1) {
                    tim[v]=tim[u]+1;
                    q.push(v);
                }
            }
            if(tim[fa[u]]>tim[u]+1 && fa[u]) {
                tim[fa[u]]=tim[u]+1;
                q.push(fa[u]);
            }
        }
    }
    return 0;
}
//
/*
6 5
1 2
2 3
2 5
1 4
4 6
*/