【08.24】牛客多校第三场G题 Yu Ling(Ling YueZheng) and Colorful Tree (CDQ分治+主席树+树链剖分)

传送门

题意

有一棵以1号节点为根的树,初始时所有节点均未染色,你可以进行两种操作:

  1. 染色操作:将一个未染色节点u,染色为x。
  2. 查询操作:查询节点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;
}
posted @ 2021-08-24 12:48  Knowledge-Pig  阅读(35)  评论(0)    收藏  举报