题解:CF1930H Interactive Mex Tree

题目传送门

好吧,我也不知道这是交互还是伪装成交互的构造了。


好吧,居然有图,我太强了。

由于是排列,所以可以把 \(\operatorname{mex}\) 转为补集的 \(\min\)

在此图中,我们就是要用 \(5\) 个区间来覆盖 \(rt,A,B,C1,C2,D1,D2,E,F,G\)\(10\) 个区域。

那么,先尝试使用 dfs 入栈序作为 \(p_1\)

假设从左到右遍历这些边,这些部分在 dfs 序上连续的有 \((rt,A),(F),(C1),(C2,E),(D1),(D2,G,B)\)

发现要用 \(6\) 次才行。

那么,考虑使用第二个序列。尝试使用出栈序来刻画。

这些部分在 dfs 出栈序上连续的有 \((A,F,C1),(C2),(E,D1),(D2),(G),(B,rt)\)

权衡利弊得到最终策略:

入栈序:\((D2,G,B),(C2,E),(rt,A)\)

出栈序:\((A,F,C1),(E,D1)\)

实现:求 \(\operatorname{lca}\)\(\operatorname{lca}\) 的两个儿子,另外,弄清楚出栈序和入栈序中 \(A,B,C1,C2,D1,D2,E,F,G,rt\) 对应的范围。实在不行看代码注释。


Plus:

还有可能 \(u\)\(v\) 的祖先。

入栈连续的有 \((rt,A),(C1),(D1),(D2,C2,B)\)

出栈连续的有 \((A,C1,D1),(D2),(C2),(B,rt)\)

选择入栈 \((D2,C2,B),(rt,A)\)

选择出栈 \((A,C1,D1)\)

时间复杂度 \(O(q\log n)\)

#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int n,q,h[N],idx,e[N<<1],ne[N<<1];
int f1[N],g1[N],f2[N],g2[N],t1,t2;
int top[N],dep[N],sz[N],son[N],fa[N];//树剖
inline void add(int a,int b) {e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void dfs1(int u){
    g1[f1[u]=++t1]=u;//入栈序
    sz[u]=1,son[u]=0;
    for (int i=h[u];i;i=ne[i]){
        int v=e[i];
        if (v==fa[u]) continue;
        fa[v]=u,dep[v]=dep[u]+1;
        dfs1(v),sz[u]+=sz[v];
        if (sz[v]>sz[son[u]]) son[u]=v;
    }
    g2[f2[u]=++t2]=u;//出栈序
}
void dfs2(int u,int tp){
    top[u]=tp;
    if (son[u]) dfs2(son[u],tp);
    for (int i=h[u];i;i=ne[i]){
        int v=e[i];
        if (v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
inline int LCA(int x,int y){
    while (top[x]^top[y]) {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
inline int query(int t,int l,int r){//封装查询
    if (l>r) return n;
    cout<<"? "<<t<<" "<<l<<" "<<r<<endl; 
    int res;cin>>res;assert(res!=-1);
    return res;
}
inline int MA(int x,int lca){//求lca到x上的lca的儿子
    while (top[x]!=top[lca]){
        if (fa[top[x]]==lca) return top[x];
        x=fa[top[x]];
    }
    return son[lca];
}
inline void work(){
    cin>>n>>q;idx=1;t1=t2=0;
    for (int i=1;i<=n;i++) h[i]=0;
    for (int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        add(u,v),add(v,u);
    }
    dfs1(1),dfs2(1,1);
    for (int i=1;i<=n;i++) cout<<g1[i]<<" ";cout<<endl;
    for (int i=1;i<=n;i++) cout<<g2[i]<<" ";cout<<endl;
    while (q--){
        int u,v;cin>>u>>v;
        if (f1[v]<f1[u]) swap(u,v);
        int lca=LCA(u,v),res=n;
        if (lca!=u){
            int U=MA(u,lca),V=MA(v,lca);
            res=min(res,query(1,f1[v]+1,n));//[D2,G,B]
            res=min(res,query(1,f1[u]+1,f1[V]-1));//[C2,E]
            res=min(res,query(1,1,f1[lca]-1));//[rt,A]
            res=min(res,query(2,1,f2[u]-1));//[A,F,C1]
            res=min(res,query(2,f2[U]+1,f2[v]-1));//[E,D1]
        } 
        else {
            res=min(res,query(1,f1[v]+1,n));//[D2,C2,B]
            res=min(res,query(1,1,f1[u]-1));//[rt,u]
            res=min(res,query(2,1,f2[v]-1));//[A,C1,D1]
        }
        cout<<"! "<<res<<endl;
        cin>>res;//注意,这里要读入一个1!!!
    }
}
int main(){
    int T;cin>>T;
    while (T--) work();
    return 0;
}
posted @ 2026-03-30 16:42  TP2010  阅读(8)  评论(0)    收藏  举报