【08.24】牛客多校第三场G题 Yu Ling(Ling YueZheng) and Colorful Tree (CDQ分治+主席树+树链剖分)
题意
有一棵以1号节点为根的树,初始时所有节点均未染色,你可以进行两种操作:
- 染色操作:将一个未染色节点u,染色为x。
- 查询操作:查询节点u的最近一个祖先节点v,满足v节点已染色,且颜色属于[l,r]区间,而且颜色是x的倍数。如果存在,输出u到v的距离, 否则输出"Impossible!"
\(1\leq n, q\leq 110000,\ 1\leq u,x,l,r\leq n,\ 1\leq w\leq 10^9\)
数据保证对于染色操作,每种颜色只会出现一次。
题解
官方题解给出了bitset+分块的做法,但我原本思路没往那块去想,所以也没去学,这里留个坑吧,之后有时间再学。
这题思路其实并不复杂,难点在于查询操作有一堆限制。总的来说有三个限制:祖先限制,颜色区间限制,颜色倍数限制。
首先考虑查询操作中\(x>\sqrt{n}\)的询问,因为此时x的倍数不超过\(\sqrt{n}\)个,所以可以直接暴力枚举在[l,r]区间中x整倍数的数。这时枚举的颜色已经满足后两个限制了,只要再判断该颜色对应的节点是否是u的祖先即可,为了控制复杂度,可以用dfs序O(1)时间内判断。节点v是u的祖先不好判断,那么可以反过来,判断u是不是在v的子树内,也就是判断\(dfn[u]\in [dfn[v],dfn[v] + size[v] - 1]\),这样对于一次\(x>\sqrt{n}\)的查询操作可以在 \(O(\sqrt{n})\)内解决。
对于\(x<=\sqrt{n}\)的查询操作,就要稍微麻烦一点了。我想到的办法需要离线处理,对于查询操作中\(x=k\)的询问,我们存入k操作序列,对于每个染色操作若\(k|x\),我们也加入k操作序列。之后从1到\(\sqrt{n}\)枚举k操作序列,对于每个操作序列分别处理。每个操作序列涉及到时间维度、颜色维度、dfs序维度,于是可以想到cdq分治一下。对于每次分治,我们用左半序列的染色操作建立一棵主席树,右半序列的查询操作去查最近的祖先。具体地说,左半序列时间维度上已经小于右半序列,再将左半序列按照颜色排序,依次建立主席树,每次将序列的dfs序插入线段树上,线段树维护某一dfs序区间上插入数的个数。右半区间上的查询操作,u节点不断往根节点上跳,每次查询主席树(rt[l-1],rt[r])这段上对应的dfs序区间个数为1的最大的那个下标值。为什么是dfs序最大?因为在u的所有祖先中由近到远dfs序是递减,最近的满足限制的祖先一定是dfs序最大那个。
因为cdq分治是一个log,主席树又是一个log,树剖往上跳jump又是一个log,故一次查询操作是\(O(\log^3 n)\)。
而且对于一次染色操作只要满足x是k的倍数,就会被加入k操作序列,也就是说一个染色操作可以加入多个操作序列中,大概还会有6倍常数。
至于为什么能过?因为跑不满,总共只有110000次操作,只有查询操作是3log的,如果查询操作一多,则染色操作就会少,6倍常数就取不到。染色操作少的话,线段树中0的个数就多,查询时一下就会返回。树剖往上跳时,只要找到一个满足条件的就会停止,不会每次都跳到根。最后我的代码跑出来,时间为959ms,在提交记录里按运行时间排序排在了第一页。
代码
/*************************************************************************
> File Name: 1.cpp
> Author: Knowledge-Pig
> Mail: 925538513@qq.com
> Blog: https://www.cnblogs.com/Knowledge-Pig/
> Created Time: 2021年08月23日 星期一 19时00分01秒
************************************************************************/
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define LL long long
#define pb push_back
#define fi first
#define se second
#define pr pair<int,int>
#define mk(a,b) make_pair(a,b)
#define endl '\n'
using namespace std;
const int maxx = 2e5 + 10;
const LL inf = 1e18;
int n, q, m = 0, tot = 0, dfn[maxx], rk[maxx], fa[maxx], son[maxx], sz[maxx], jump[maxx], c[maxx];
int rt[maxx], tr[maxx * 30], ls[maxx * 30], rs[maxx * 30], w[maxx * 30];
pr b[maxx];
LL dep[maxx], ans[maxx];
struct ask{
int t, u, l, r;
};
vector<pr> vec[maxx];
vector<ask> a[305];
void dfs1(int id){
sz[id] = 1;
for(auto u: vec[id])
if(u.fi != fa[id]){
dep[u.fi] = dep[id] + u.se;
fa[u.fi] = id;
dfs1(u.fi);
sz[id] += sz[u.fi];
if(sz[u.fi] > sz[son[id]]) son[id] = u.fi;
}
}
void dfs2(int id, int top){
dfn[id] = ++tot;
rk[tot] = id;
jump[id] = top;
if(son[id]) dfs2(son[id], top);
for(auto u: vec[id])
if(u.fi != fa[id] && u.fi != son[id])
dfs2(u.fi, u.fi);
}
void solve_0(){
int u, x;
cin >> u >> x;
c[x] = u;
for(int i = 1; i <= min(x, 300); ++i)
if(x % i == 0) a[i].pb((ask){0, u, x, 0});
}
void solve_1(int id){
int u, l, r, x;
cin >> u >> l >> r >> x;
if(x > 300){
for(int i = (l + x - 1) / x * x; i <= r; i += x){
int j = c[i];
if(j > 0 && dfn[j] <= dfn[u] && dfn[u] < dfn[j] + sz[j])
ans[id] = min(ans[id], dep[u] - dep[j]);
}
}
else a[x].pb((ask){id, u, l, r});
}
void insert(int &q, int pre, int l, int r, int x){
q = ++tot; ls[q] = ls[pre]; rs[q] = rs[pre]; w[q] = w[pre] + 1;
if(l != r){
int mid = l + r >> 1;
if(x <= mid) insert(ls[q], ls[pre], l, mid, x);
else insert(rs[q], rs[pre], mid + 1, r, x);
}
}
int query(int ll, int rr, int l, int r, int ql, int qr){
int val = w[rr] - w[ll];
if(!val) return 0;
else if(val == r - l + 1) return qr;
int mid = l + r >> 1;
if(ql > mid) return query(rs[ll], rs[rr], mid + 1, r, ql, qr);
else if(qr <= mid) return query(ls[ll], ls[rr], l, mid, ql, qr);
else{
int tmp = query(rs[ll], rs[rr], mid + 1, r, mid + 1, qr);
if(tmp > 0) return tmp;
else return query(ls[ll], ls[rr], l, mid, ql, mid);
}
}
void cdq(int x, int L, int R){
if(L == R) return;
int mid = L + R >> 1;
cdq(x, L, mid); cdq(x, mid + 1, R);
int cnt = 0;
for(int i = L; i <= mid; ++i)
if(!a[x][i].t) b[++cnt] = mk(a[x][i].l, a[x][i].u);
sort(b + 1, b + cnt + 1);
tot = 0;
for(int i = 1; i <= cnt; ++i){
insert(rt[i], rt[i - 1], 1, n, dfn[b[i].se]);
c[i] = b[i].fi;
}
for(int i = mid + 1; i <= R; ++i)
if(a[x][i].t > 0){
int l = lower_bound(c + 1, c + cnt + 1, a[x][i].l) - c;
int r = upper_bound(c + 1, c + cnt + 1, a[x][i].r) - c - 1;
int u = a[x][i].u;
if(l > r) continue;
while(u){
int pos = query(rt[l - 1], rt[r], 1, n, dfn[jump[u]], dfn[u]);
if(pos){
ans[a[x][i].t] = min(ans[a[x][i].t], dep[a[x][i].u] - dep[rk[pos]]);
break;
}
u = fa[jump[u]];
}
}
for(int i = 1; i <= tot; ++i) w[i] = ls[i] = rs[i] = 0;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
cin >> n >> q;
for(int i = 1, u, v, z; i < n; ++i){
cin >> u >> v >> z;
vec[u].pb(mk(v,z));
vec[v].pb(mk(u,z));
}
for(int i = 1; i <= n; ++i) ans[i] = inf;
dfs1(1); dfs2(1, 1);
for(int i = 1, t; i <= q; ++i){
cin >> t;
if(!t) solve_0();
else solve_1(++m);
}
for(int i = 1; i <= 300; ++i){
if(a[i].size() == 0) continue;
cdq(i, 0, a[i].size() - 1);
}
for(int i = 1; i <= m; ++i)
if(ans[i] == inf) cout << "Impossible!" << endl;
else cout << ans[i] << endl;
return 0;
}

浙公网安备 33010602011771号