2026“钉耙编程”春季联赛(7)1001思路分享(数论,分层图最短路)

题意概述

给定一个 \(n\) 个点 \(m\) 条边的无向连通图,每条边有一个正权值,表示经过该边所耗费的时间。你初始位于 \(1\) 号点,初始魔力值为 \(0\)

在第 \(i\) 个点上,有一种魔力效果为 \(a_i\) 的药水,无限量供应,喝下药水不耗费时间。每喝一瓶药水,你可以选择使魔力值增加 \(a_i\) 或减少 \(a_i\)。任意时刻魔力值可以为负数。

你的目标是通过在图上移动和喝药水,使魔力值恰好等于给定的目标值 \(V\)。求所需的最少总时间。若无论如何都无法达到目标,输出 \(-1\)

保证:\(a_1\) 的所有质因数均包含在 \(V\) 的质因数集合中。

多测,\(1 \le T \le 10,1 \le n, m \le 10^4\)

思路

假设可以使用 \(b_1,b_2 \cdots b_k\) 的药水,根据裴蜀定理,能凑出 \(V\) 的充要条件是:

\[\gcd(b_1, b_2, \cdots, b_k) \mid V \]

题目保证了 \(a_1\) 的所有质因数均包含在 \(V\) 的质因数集合中,因此对于 \(V\) 的所有质因数,只要存在一个 \(b_i\),其对应该质因数的个数 \(\le V\) 对应该质因数的个数即可。

不难发现 \(V\) 的质因数个数不会太多,可以状压。

定义 \(mask_i\)\(a_i\) 可以满足 \(V\) 质因数限制的状态,比如 \(mask_{i,j} = 1\) 表示:对于 \(V\) 的第 \(j\) 个质因数,\(a_i\) 对应该质因数的个数 \(\le V\) 对应该质因数的个数。

预处理出 \(mask\),然后跑分层图最短路即可。

质因数个数不会超过 \(8\),时间复杂度 \(\mathcal{O}(2^8 \cdot (n+m) \log(n \cdot 2^8))\)

代码

//author:kzssCCC

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

class sieve{
public:
	int n;
	vector<int> prime;
	vector<bool> f;
	vector<vector<int>> factors;
	vector<vector<pair<int,int>>> factors_cnt;
	vector<ll> phi;
	
	sieve(int _n){
		n = _n;

		phi = vector<ll>(n+1);
		phi[1] = 1;

		f = vector<bool>(n+1,true);
		f[0] = f[1] = false;
		
		for (int i=2;i<=n;i++){
			if (f[i]){
				prime.push_back(i);
				phi[i] = i-1;
			}
			
			for (auto& v:prime){
				if ((ll)v*i>n) break;
				f[v*i] = false;

				if (i%v==0){
					phi[v*i] = phi[i]*v;
					break;
				}

				phi[v*i] = phi[i]*phi[v];
			}
		}
		
		// init_factors();
	}
	
	
	vector<int> factorize(ll x){
		vector<int> res;
		
		for (auto& v:prime){
			if ((ll)v*v>x) break;
			if (x%v==0){
				res.push_back(v);
				while (x%v==0) x/=v;
			}
		}
		if (x>1) res.push_back(x);
		
		return res;
	}
	
	vector<pair<int,int>> factorize_cnt(ll x){
		vector<pair<int,int>> res;
		
		for (auto& v:prime){
			if ((ll)v*v>x) break;
			if (x%v==0){
				pair<int,int> pr{v,0};
				while (x%v==0){
					pr.second++;
					x/=v;
				}
				res.push_back(pr);
			}
		}
		if (x>1) res.emplace_back(x,1);
		return res;
	}
	
	void init_factors(){
		factors = vector<vector<int>>(n+1);
		factors_cnt = vector<vector<pair<int,int>>>(n+1);
		
		for (int i=1;i<=n;i++){
			factors[i] = factorize(i);
			factors_cnt[i] = factorize_cnt(i);
		}
	}
};	

sieve se(2e5);
const ll INF = 9e18;

void solve(){
	int n,m;
	ll V;
	cin >> n >> m >> V;

	vector<ll> a(n+1);
	for (int i=1;i<=n;i++){
		cin >> a[i];
	}

	vector<vector<pair<ll,int>>> adj(n+1);
	for (int i=0;i<m;i++){
		int u,v;
		ll w;
		cin >> u >> v >> w;

		adj[u].emplace_back(w,v);
		adj[v].emplace_back(w,u);
	}

	auto fac = se.factorize_cnt(V);
	int len = fac.size();

	vector<int> mask(n+1);

	for (int i=1;i<=n;i++){
		auto temp = se.factorize_cnt(a[i]);
			
		for (int j=0;j<len;j++){
			auto& [v,c] = fac[j];

			auto it = lower_bound(temp.begin(),temp.end(),pair<int,int>(v,0));
			if (it==temp.end() || it->first!=v || it->second<=c){
				mask[i] |= 1<<j;			
			}
		}
	}

	vector<vector<ll>> dis(n+1,vector<ll>(1<<len,INF));
	priority_queue<array<ll,3>,vector<array<ll,3>>,greater<>> pq;

	dis[1][mask[1]] = 0;
	pq.push({0,1,mask[1]});

	while (!pq.empty()){
		auto [d,u,sta] = pq.top();
		pq.pop();

		if (d>dis[u][sta]) continue;

		for (auto& [w,v]:adj[u]){
			int nsta = sta | mask[v];

			if (d+w<dis[v][nsta]){
				dis[v][nsta] = d+w;
				pq.push({dis[v][nsta],v,nsta});
			}
		}
	}

	ll res = INF;
	for (int i=1;i<=n;i++){
		res = min(res,dis[i][(1<<len)-1]);
	}

	if (res==INF){
		cout << -1 << '\n';
	}
	else{
		cout << res << '\n';
	}
}	

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

	return 0;
}
posted @ 2026-05-11 17:12  kzssCCC  阅读(13)  评论(0)    收藏  举报