旅行 (拆点分层)、求1-n中每个数的所有质因数

https://ac.nowcoder.com/acm/contest/37782/I

题意
给定 n 个点 m 个边的无向图,每个边有花费 w i w_i wi
起点为 1,终点为 n。
对于从起点到终点的一条路径来说,走到第偶数个点要花费 x x x
问,从起点到终点的最少花费为多少?
( 1 ≤ n , x ≤ 1 0 5 ,   n − 1 ≤ m ≤ 2 × 1 0 5 ) (1≤n,x≤10^5,\ n−1≤m≤2×10^5) (1n,x105, n1m2×105)

思路
分析题目知道,对于一条路径来说,其花费不仅依赖于边权之和,还要依赖于路径长度。路径越长,偶数点越多,就要多花费若杠个 x。所以不能简单的跑最短路。

从一个点到下一个点的花费是不同的,除了边权之外,还要考虑当前点是奇数点还是偶数点。奇数点和偶数点相连。

可以把每个点拆成两个点,分别是奇数点和偶数点。
将所有的奇数点放在第一层,所有偶数点放在第二层。每次从第一层向第二层连边,从第二层向第一层连边,这样便是奇数点和偶数点连边。
因为到偶数点要花费 x,可以把这个花费加到边权上,从第一层向第二层连边时,边权加上 x。

然后从第一层的1号点开始跑,取到第一层的 n 号点和第二层的 n 号点的最短距离最小值即为最小花费。

Code

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

#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define int long long

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];
int k;
int dist[N], f[N];
vector<PII> e[N];
int ans;

void dij()
{
	priority_queue<PII, vector<PII>, greater<PII> > que;
	que.push({0, 1});
	
	for(int i=1;i<=2*n;i++) dist[i] = 1e18;
	dist[1] = 0;
	
	while(que.size())
	{
		int x = que.top().se; que.pop();
		if(f[x]) continue;
		f[x] = 1;
		
		if(x == n || x == n + n){
			ans = dist[x];
			return;
		}
		
		for(auto it : e[x])
		{
			int tx = it.fi, dis = it.se;
			if(dist[tx] > dist[x] + dis){
				dist[tx] = dist[x] + dis;
				que.push({dist[tx], tx});
			}
		}
	}
}

signed main(){
	Ios;
	cin >> n >> m >> k;
	while(m--)
	{
		int x, y, z; cin >> x >> y >> z;
		int nex = n + x, ney = n + y;
		e[x].push_back({ney, z + k});
		e[ney].push_back({x, z});
		e[nex].push_back({y, z});
		e[y].push_back({nex, z + k});
	}
	
	dij();
	
	cout << ans;
	
	return 0;
}

经验
拆点的思想要掌握。


求1-n中每个数的所有质因数

直接每个数分解质因数的复杂度是 O ( n n ) O(n \sqrt n) O(nn ),1e6 就跑不过去了。

可以反过来想,考虑每个质因数在哪些数中。
对于每个质因数来说,其肯定是其所有倍数的质因数。
那么就可以在埃式筛里面,遍历其所有的倍数,把当前质因子放到该倍数的vector里,同时将该数标记为非质数,不会遍历其倍数。
这样,埃式筛保证了遍历的是质数,遍历所有倍数保证了是因子,在 O ( n l o g n ) O(nlogn) O(nlogn) 的复杂度内就能找到 1-n 这 n 个数的所有质因子。

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

const int N = 2000010, mod = 1e9+7;
int T, n, m;
vector<int> v[N];
int f[N];

void Prim()
{
	for(int i=2;i<=n;i++)
	{
		if(f[i]) continue;
		v[i].push_back(i);
		
		for(int j=i+i;j<=n;j+=i){
			v[j].push_back(i);
			f[j] = 1;
		}
	}
}

signed main(){
	cin >> n;
	Prim();
	
	for(int i=1;i<=n;i++)
	{
		cout << i << " : ";
		for(int x : v[i]) cout << x << " ";
		cout << endl;
	}
	return 0;
}

神奇数字

题意
找出所有正整数 x ,使得 a ≡ b ≡ c   (   m o d   x ) a\equiv b \equiv c\ (\bmod x) abc (modx)
请从小到大输出所有 x 可能的取值,如果有无限种可能的 x,则输出 -1 。

思路
先对于两个数找到 x,再判断另一个数符不符合。
a ≡ b   (   m o d   x ) a\equiv b\ (\bmod x) ab (modx) 转化为 ( a − b )   m o d   x = 0 (a-b)\bmod x = 0 (ab)modx=0
那么 x 是 ∣ a − b ∣ |a-b| ab 的约数。
遍历其所有约数看 c 是否符合即可。

当 a=b=c 时,x 可取任意值。
否则取两个不同的数相减求约数。

Code

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

const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N];

signed main(){
	cin >> T;
	while(T--)
	{
		int x, y, z;
		cin >> x >> y >> z;
		if(x == y && y == z){
			cout << -1 << endl;
			continue;
		}
		
		set<int> st;
		
		int n;
		if(x != y) n = abs(x - y);
		else n = abs(x - z);
		
		for(int i=1;i<=n/i;i++)
		{
			if(n % i == 0)
			{
				if(x % i == y % i && x % i == z % i){
					st.insert(i);
				}
				int tt = n/i;
				if(tt == i) continue;
				if(x % tt == y % tt && x % tt == z % tt){
					st.insert(tt);
				}
			}
		}
		for(int x : st) cout << x << " ";
		cout << endl;
	}
	
	return 0;
}

x mod y = 0, y一定是 x 的约数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值