首页 > 其他分享 >[题解]AT_abc267_f [ABC267F] Exactly K Steps

[题解]AT_abc267_f [ABC267F] Exactly K Steps

时间:2024-06-22 14:09:42浏览次数:6  
标签:int 题解 Exactly sqrt 查询 Steps 深度 根时

大家好,我是毒瘤,喜欢用玄学算法过题。

发现题解区没有这个做法,于是来发一篇。

思路

不难发现如果一个点对 \((u,v)\) 的距离为 \(d\),那么在这棵树以 \(u\) 为根时,\(v\) 的深度为 \(d\)。于是考虑换根 DP。

首先思考如何计算答案。显然我们可以将查询离线下来,然后当换根到以 \(u\) 为根时,将有关 \(u\) 的所有查询全部解决即可。

其次,发现当一个 \(v\) 转化成根时,它会在其父节点 \(u\) 为根时的形态的基础上,所在 \(v\) 子树中的节点深度减 \(1\),其余节点加 \(1\)。

然而,我们需要求的是深度为 \(d\) 的节点,于是单单想大多数的换根 DP 维护深度的极值是不行的,所以需要更新出所有的深度。

于是这个东西需要使用数据结构维护。明确这个数据结构所需的功能:

  1. 区间加减。
  2. 区间查询权值为 \(k\) 的编号。

于是你就想到了这道题。即用分块实现,一个 vector 维护块内的元素,每一次每一次修改都需要一次排序,查询都需要一次二分,单次操作是 \(\Theta(\sqrt n \log \sqrt n)\) 的。配合上巨大常数,你得到了 TLE 15 的代码。

然后你可以发现此题是维护深度,所以值域很小,所以可以开桶维护。你在查询的时候每次都先看一下在这块中有没有出现 \(k\),如果出现了就直接暴力找;否则就继续向前面的块判断。这样实现即可单次操作做到 \(\Theta(\sqrt n)\)。

于是你就得到了理论时间复杂度为 \(\Theta(q \sqrt n)\) 的代码,但是常数有点小大,但是发现每一次的桶都不需要清空完,只需将原本的 \(val\) 删掉,然后加上新的 \(val\) 即可。

Code

#include <bits/stdc++.h>  
#define fst first  
#define snd second  
#define re register  
  
using namespace std;  
  
typedef pair<int,int> pii;  
const int N = 2e5 + 10,M = 4e5 + 10,K = 1010;  
int n,q;  
int idx,h[N],ne[M],e[M],ans[N];  
int num,id[N],pid[N],sz[N],d[N],val[N];  
vector<pii> Q[N];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 3) + (r << 1) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline void add(int a,int b){  
    ne[idx] = h[a];  
    e[idx] = b;  
    h[a] = idx++;  
}  
  
struct block{  
    int len,num;  
    int pos[N],L[K],R[K],tag[K],vis[K][N];  
  
    inline void init(){  
        len = num = sqrt(n);  
        for (re int i = 1;i <= n;i++) val[id[i]] = d[i];  
        for (re int i = 1;i <= num;i++){  
            L[i] = (i - 1) * len + 1;  
            R[i] = i * len;  
        }  
        if (R[num] != n){  
            num++;  
            L[num] = (num - 1) * len + 1;  
            R[num] = n;  
        }  
        for (re int i = 1;i <= num;i++){  
            for (re int j = L[i];j <= R[i];j++){  
                pos[j] = i;  
                vis[i][val[j]]++;  
            }  
        }  
    }  
  
    inline void modify(int l,int r,int k){  
        int p = pos[l],q = pos[r];  
        if (p == q){  
            for (re int i = l;i <= r;i++){  
                vis[p][val[i]]--;  
                val[i] += k;  
                vis[p][val[i]]++;  
            }  
        }  
        else{  
            for (re int i = l;i <= R[p];i++){  
                vis[p][val[i]]--;  
                val[i] += k;  
                vis[p][val[i]]++;  
            }  
            for (re int i = p + 1;i < q;i++) tag[i] += k;  
            for (re int i = L[q];i <= r;i++){  
                vis[q][val[i]]--;  
                val[i] += k;  
                vis[q][val[i]]++;  
            }  
        }  
    }  
  
    inline int query(int l,int r,int k){  
        int p = pos[l],q = pos[r];  
        if (p == q){  
            for (re int i = l;i <= r;i++){  
                if (val[i] + tag[p] == k) return pid[i];  
            }  
        }  
        else{  
            for (re int i = l;i <= R[p];i++){  
                if (val[i] + tag[p] == k) return pid[i];  
            }  
            for (re int i = p + 1;i < q;i++){  
                if (vis[i][k - tag[i]]){  
                    for (re int j = L[i];j <= R[i];j++){  
                        if (val[j] + tag[i] == k) return pid[j];  
                    }  
                }  
            }  
            for (re int i = L[q];i <= r;i++){  
                if (val[i] + tag[q] == k) return pid[i];  
            }  
        }  
        return -1;  
    }  
}bl;  
  
inline void calc(int u){  
    for (auto x:Q[u]) ans[x.snd] = bl.query(1,n,x.fst + 1);  
}  
  
inline void dfs1(int u,int fa){  
    num++;  
    sz[u] = 1;  
    id[u] = num;  
    pid[num] = u;  
    d[u] = d[fa] + 1;  
    for (re int i = h[u];~i;i = ne[i]){  
        int j = e[i];  
        if (j == fa) continue;  
        dfs1(j,u);  
        sz[u] += sz[j];  
    }  
}  
  
inline void dfs2(int u,int fa){  
    for (re int i = h[u];~i;i = ne[i]){  
        int j = e[i];  
        if (j == fa) continue;  
        bl.modify(1,n,1);  
        bl.modify(id[j],id[j] + sz[j] - 1,-2);  
        calc(j);  
        dfs2(j,u);  
        bl.modify(1,n,-1);  
        bl.modify(id[j],id[j] + sz[j] - 1,2);  
    }  
}  
  
int main(){  
    memset(h,-1,sizeof(h));  
    n = read();  
    for (re int i = 1;i < n;i++){  
        int a,b;  
        a = read();  
        b = read();  
        add(a,b);  
        add(b,a);  
    }  
    q = read();  
    for (re int i = 1;i <= q;i++){  
        int a,b;  
        a = read();  
        b = read();  
        Q[a].push_back({b,i});  
    }  
    for (re int i = 1;i <= n;i++) sort(Q[i].begin(),Q[i].end());  
    dfs1(1,0);  
    bl.init();  
    calc(1);  
    dfs2(1,0);  
    for (re int i = 1;i <= q;i++) printf("%d\n",ans[i]);  
    return 0;  
}  

标签:int,题解,Exactly,sqrt,查询,Steps,深度,根时
From: https://www.cnblogs.com/WaterSun/p/18262224

相关文章

  • BD202301·公园题解
    BD202301·公园题解考虑将整个移动过程分为两个部分:小度和度度熊汇合之前小度和度度熊汇合之后第一部分可以直接用Dijkstra算法直接搞定,第二部分可以考虑反向思考,从N点出发做一次Dijkstra,最后枚举每个汇合点即可得到答案。时间复杂度\(\Theta(nlogn)\)代码如下:#include......
  • [题解]AT_abc264_e [ABC264E] Blackout 2
    思路一道很经典的题,运用了一种叫「时光倒流」的技巧。「时光倒流」本质上就是将所有删边(或删点)的操作,通过倒序循环求值的方式转化为加边(或加点)。「时光倒流」具体实现通常伴随着并查集出现,维护一个连通块的某种性质。首先,我们需要将所有从始至终没有删过的边加入并查集。在这......
  • [题解]AT_abc263_d [ABC263D] Left Right Operation
    思路首先,不难发现最终的序列一定是形如下面的序列:\[l,\dots,l,a_i,a_{i+1},\dots,a_{i+j},r,\dotsr\]那么,我们就可以将其分为三段,每段都单独维护。首先,对于第一段,我们可以枚举出最后一个\(l\)的位置\(x\),那么和为\(x\timesl\)。对于第二段显然可以用前......
  • [题解]AT_abc256_h [ABC256Ex] I like Query Problem
    思路首先可以看一下P4145,在P4145中使用了一种叫势能线段树的Trick。对于势能线段树,我个人的理解是,对于一段区间(或一个点)直接暴力维护,在经过很少的次数后操作将没有意义的题就可以使用势能线段树。在本题中,如果没有推平操作,显然我们可以直接使用势能线段树,时间复杂度可以轻......
  • [题解]AT_abc263_f [ABC263F] Tournament
    先为大家毙掉一个错解思路首先不难发现,如果将整棵比赛的对战图画出来,一定是一个满二叉树。不妨将令一个节点\(u\)的左右儿子编号分别为\(2u\)和\(2u+1\)。然后定义\(dp_{u,d}\)表示将\(u\)为根的子树内的选手全部比赛完,并且\(u\)已经赢了\(d\)场的最大结果。发......
  • [题解]AT_abc237_g [ABC237G] Range Sort Query
    思路将小于等于\(x\)的元素赋为\(1\),其余的赋为\(0\)。那么一个区间内小于等于\(x\)的数量就是区间中\(1\)的数量。那么,将区间升序排列就是将\(1\)先堆在前面,将\(0\)堆到后面;降序排列同理。考虑动态维护\(x\)的位置,记其位置为\(t\)。如果\(l\leqt\leqr\),则......
  • [题解]AT_abc236_f [ABC236F] Spices
    思路首先对所有的\(c\)从小到大排序,然后对于每一个值如果之前能凑出就不选,否则就选。这样做显然是对的。令\(p_1,p_2,\dots,p_{2^n-1}\)表示将\(c\)排序之后,对应原来的下标;\(S\)表示选出数的集合;\(S'\)表示最终选出数的集合。可以证明两个问题:如果\(p_i\)可以被已选......
  • [题解]AT_abc247_f [ABC247F] Cards
    思路对于包含数\(x\)的卡牌,两张之中必定要选择一张,由此想到2-SAT的思想。我们将所有带有\(x\)的卡牌两两连边,每一条边连接的点都表示两点必须选择一个。不难发现,我们这样会得出若干个环。(因为对于每一张卡牌的出边为\(2\),一定会形成环)在每一个环中的选择情况,不会影响答......
  • [题解]AT_abc240_f [ABC240F] Sum Sum Max
    思路题目要求的是\(\max_{a=1}^{n}\{\sum_{i=1}^{a}\sum_{j=1}^{a}{A_j}\}\),所以我们将\(\sum_{i=1}^{a}\sum_{j=1}^{a}{A_j}\)化简一下,得:\[i\timesA_1+(i-1)\timesA_2+\dots+1\timesA_x\]在\(a\)每增加\(1\)时,这个和\(s\)将会变......
  • [题解]AT_abc250_d [ABC250D] 250-like Number
    思路对于这道题,我们可以发现一个事情:我们筛质数只需要筛\(1\sim\log_3n\)的部分就行了。因为\(k=p\timesq^3\),那么,我们考虑一种极端情况,\(p\)为一个很小的数,那么\(k\)就无限接近于\(q^3\)。我们就先假设\(k=q^3\),那么可以得出\(q=\log_3n\)。然后由题目描......