CF2223C 思路分享(数论)

https://codeforces.com/problemset/problem/2223/C

题意

给定一棵 \(n\) 个节点的树,根为 \(1\),每条边有边权 \(w\),走过这条边需要 \(w\) 时间.

当时间为 \(T\) 时,节点 \(u\) 的指示节点为 \(u\) 的第 \(T \mod d + 1\) 个孩子,\(d\)\(u\) 的孩子数量.

给定 \(q\) 个询问,每个询问给定初始时间 \(m_i\),求从根出发沿着指示节点最后会到达的节点.

\(1\le n \le 5\cdot 10^5\)\(1\le q \le 10^6\)\(0\le m_i \le 10^{18}\).

思路

假如当前时间为 \(T\),在节点 \(u_1\),则

\[\begin{cases} T \equiv r_1 \pmod{deg_{u_1}} \\ (T+w_{u_1,r_1}) \equiv r_2 \pmod{deg_{u_2}} \\ \cdots \end{cases} \]

后面项都可以化成与第一项类似的形式,发现本质是解同余方程,启发我们用类似拓展中国剩余定理的形式维护询问.

对于节点 \(u\),维护会到达 \(u\) 的所有询问,距离 \(dis\),以及二元组 \((M,r)\),会到达 \(u\) 的所有询问满足

\[m_i \equiv r \pmod M \]

  • \(M\gt 10^{18}\)\(deg_u \mid M\),则

\[(m_i+dis)\mod {deg_u} = (r+dis)\mod {deg_u} \]

为定值,所有询问会前往相同的孩子.

  • 否则维护的询问会分裂,将 \(M\) 迭代成 \(lcm(M,deg_u)\),同时调整 \(r\).

每次分裂,\(M\) 至少会 \(\times 2\),因此分裂次数是 \(log\) 级别的.

时间复杂度 \(\mathcal{O}(n \log V)\)\(V\)\(m_i\) 的值域.

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;

const ll INF = 1e18;
const ll MOD = 1e18+7;

ll lcm(ll a,ll b){
	i128 temp = (i128)a/__gcd(a,b)*b;
	if (temp>INF){
		return MOD;
	}
	else{
		return temp;
	}
}

void solve(){
	int n,q;
	cin >> n >> q;

	vector<int> F(n+1);
	vector<ll> L(n+1);
	vector<vector<pair<ll,int>>> adj(n+1);
	for (int i=2;i<=n;i++){
		cin >> F[i];
	}
	for (int i=2;i<=n;i++){
		cin >> L[i];
		adj[F[i]].emplace_back(L[i],i);
	}
	for (int i=1;i<=n;i++){
		sort(adj[i].begin(),adj[i].end(),[&](auto& p1,auto& p2){
			return p1.second < p2.second;
		});
	}

	vector<ll> m(q+1);
	for (int i=1;i<=q;i++){
		cin >> m[i];
	}
	vector<int> res(q+1);

	function<void(int,ll,ll,ll,vector<int>&)> dfs = [&](int u,ll dis,ll M,ll r,vector<int>& Q){
		int deg = adj[u].size();
		if (deg==0){
			for (auto& i:Q){
				res[i] = u;
			}
			return;
		}

		if (M>INF || M%deg==0){
			dfs(adj[u][(r+dis)%deg].second,dis+adj[u][(r+dis)%deg].first,M,r,Q);
		}	
		else{
			vector<vector<int>> temp(deg);
			for (auto& i:Q){
				temp[(m[i]+dis)%deg].push_back(i);				
			}

			for (int i=0;i<deg;i++){
				if (temp[i].empty()) continue;
				auto& [w,v] = adj[u][i];
				ll nM = lcm(M,deg);
				dfs(v,dis+w,nM,m[temp[i][0]]%nM,temp[i]);
			}
		}
	};

	{
		vector<int> temp(q);
		iota(temp.begin(),temp.end(),1);
		dfs(1,0,1,0,temp);
	}

	for (int i=1;i<=q;i++){
		cout << res[i] << ' ';
	}
	cout << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-06-30 13:44  kzssCCC  阅读(6)  评论(0)    收藏  举报