LOJ #6669 Nauuo and Binary Tree (交互题、树链剖分)

题目链接

https://loj.ac/problem/6669

题解

Orz yyf太神了,出这种又有意思又有意义的好题造福人类……
首先\(n\)次询问求出所有节点的深度。
考虑按深度扩展(BFS), 同时维护重链剖分
每次扩展一个点时,从根节点所在重链开始,每次询问当前节点与链底节点的距离,这样就可以算出它们LCA的深度,也就是当前节点到根的路径上与这条重链相交部分的最大深度。那么如果这个最大深度等于当前深度\(-1\), 就得到了该点的父亲;否则跳到LCA的轻儿子所在重链上,继续询问即可。
时间复杂度\(O(n^2)\), 询问次数\(O(n\log n)\).

代码

比我想象中的好写一些。

#include
#define llong long long
using namespace std;

inline int read()
{
    int x = 0,f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    return x*f;
}

const int N = 3000;
int son[N+3][2];
int hvs[N+3];
int dep[N+3];
int fa[N+3];
int sz[N+3];
vector idep[N+3];
int n;

int getbtn(int u) {while(son[u][hvs[u]]) {u = son[u][hvs[u]];} return u;}
int jumpup(int u,int x) {while(x--) {u = fa[u];} return u;}
int jumpdown(int u,int x) {while(x--) {u = son[u][hvs[u]];} return u;}
void verify(int u) {hvs[u] = (sz[son[u][1]]>sz[son[u][0]]);}
void setfa(int u,int v) //fa[u]=v
{
//  printf("setfa %d %d\n",u,v);
    fa[u] = v; sz[u] = 1; son[v][son[v][0]?1:0] = u;
    while(v)
    {
        verify(v); sz[v]++; v = fa[v];
    }
}

void work(int u)
{
    int v = getbtn(1);
    while(1)
    {
        printf("? %d %d\n",v,u); fflush(stdout);
        int dis; scanf("%d",&dis);
        int deplca = (dep[u]+dep[v]-dis)>>1;
        v = jumpup(v,dep[v]-deplca);
        if(dep[u]-deplca==1) {setfa(u,v); return;}
        v = son[v][hvs[v]^1]; v = getbtn(v);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=2; i<=n; i++) {printf("? %d %d\n",1,i); fflush(stdout); scanf("%d",&dep[i]); idep[dep[i]].push_back(i);}
    for(int i=0; i

你可能感兴趣的:(LOJ #6669 Nauuo and Binary Tree (交互题、树链剖分))