[题解]AT_abc267_f [ABC267F] Exactly K Steps
大家好,我是毒瘤,喜欢用玄学算法过题。
发现题解区没有这个做法,于是来发一篇。
思路
不难发现如果一个点对 的距离为 ,那么在这棵树以 为根时, 的深度为 。于是考虑换根 DP。
首先思考如何计算答案。显然我们可以将查询离线下来,然后当换根到以 为根时,将有关 的所有查询全部解决即可。
其次,发现当一个 转化成根时,它会在其父节点 为根时的形态的基础上,所在 子树中的节点深度减 ,其余节点加 。
然而,我们需要求的是深度为 的节点,于是单单想大多数的换根 DP 维护深度的极值是不行的,所以需要更新出所有的深度。
于是这个东西需要使用数据结构维护。明确这个数据结构所需的功能:
- 区间加减。
- 区间查询权值为 的编号。
于是你就想到了这道题。即用分块实现,一个 vector
维护块内的元素,每一次每一次修改都需要一次排序,查询都需要一次二分,单次操作是 的。配合上巨大常数,你得到了 TLE 15 的代码。
然后你可以发现此题是维护深度,所以值域很小,所以可以开桶维护。你在查询的时候每次都先看一下在这块中有没有出现 ,如果出现了就直接暴力找;否则就继续向前面的块判断。这样实现即可单次操作做到 。
于是你就得到了理论时间复杂度为 的代码,但是常数有点小大,但是发现每一次的桶都不需要清空完,只需将原本的 删掉,然后加上新的 即可。
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;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18262224
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】