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)
(1≤n,x≤105, n−1≤m≤2×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)
a≡b≡c (modx) 。
请从小到大输出所有 x 可能的取值,如果有无限种可能的 x,则输出 -1 。
思路
先对于两个数找到 x,再判断另一个数符不符合。
a
≡
b
(
m
o
d
x
)
a\equiv b\ (\bmod x)
a≡b (modx) 转化为
(
a
−
b
)
m
o
d
x
=
0
(a-b)\bmod x = 0
(a−b)modx=0。
那么 x 是
∣
a
−
b
∣
|a-b|
∣a−b∣ 的约数。
遍历其所有约数看 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 的约数。