2020牛客暑期多校训练营(第三场)
E-Two Matchings
题意:
给你一个数组 a, 让你构造两个排列 p, q 且p与q不相同, 求, a[i] - a[p[i]]所有值的觉得值且最小。
题解:
仔细想一下题目中的给的条件就可以转化位 , 再一个数轴上 ,一个数与另一个数组合, 让你不重复的选n个组合
求出每两个组合之间的距离之和的最小值。
因为最数轴上肯定时离的近的距离最小, 然后你会发现, 4 个数选 4 个组合最小值一定时 ((a[4] - a[1]) * 2)
同理 6 数字 选6个组合一定时 ((a[6] - a[1]) * 2) 那8 个呢?, 8个不就是两个4个嘛, 题目给的n是偶数, 那只有两种情况了, 就是4个数字一组,和6个数字一组了。
那怎么分呢?
如果选择贪心那么肯定是错的你不知道选几个 4,和6, 所以用dp
设 (dp[i][0])表示 第i个到 i- 3选 4得到的最小值, (dp[i][1])第 i个第 i - 5选6得到的最小值。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;
int t, n;
ll a[N], dp[N][2];
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
dp[i][0] = dp[i][1] = 1e12;
}
sort(a + 1, a + n + 1);
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
if (i + 3 <= n) {
dp[i + 3][0] = min(dp[i - 1][1] + a[i + 3] - a[i], dp[i - 1][0] + a[i + 3] - a[i]);
}
if (i + 5 <= n) {
dp[i +5][1] = min(dp[i- 1][0] + (a[i + 5] - a[i]), dp[i -1][1] + a[i + 5] - a[i]);
}
}
printf("%lld
", min(dp[n][0], dp[n][1]) * 2);
}
}
题意:
给一个图,刚开始每个点再一个集合, 有q次询问, 每次查询将当前集合相连的点加入到该集合, 问q次操作和每个点属于那个集合。
题解:
直接用并查集 + 链表维护,用链边可以o(1)时间合并(尾指针连接头指针)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6;
int head[N], top = 1, n, m, fa[N], ranks[N];
list<int> q[N];
queue<int>p;
struct edge {
int u, next;
}e[2 * N];
void add_edge(int u, int v) {
e[top].u = v;
e[top].next = head[u];
head[u] = top++;
}
int find(int x) {
if (x != fa[x]) {
return fa[x] = find(fa[x]);
}
return x;
}
int main() {
int t; scanf("%d", &t);
while (t--) {
scanf("%d %d", &n, &m);
top = 1;
for (int i = 0; i < n; i++) {
q[i].clear();
}
for (int i = 0; i < n; i++) {
head[i] = 0;
fa[i] = i;
ranks[i] = 1;
q[i].push_back(i);
}
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d %d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
int Q; scanf("%d", &Q);
while (Q--) {
int x; scanf("%d", &x);
if (x != fa[x]) continue;
int fx = find(x);
int cnt = q[x].size();
while (cnt--) {
int cd = q[x].front();
q[x].pop_front();
p.push(cd);
for (int i = head[cd]; i; i = e[i].next) {
int to = e[i].u;
int fy = find(to);
if(fx != fy) {
fa[fy] = fx;
q[x].splice(q[x].end(), q[fy]);
}
}
}
}
for (int i = 0; i < n; i++) {
printf("%d ", find(i));
}
puts("");
}
}
F-Fraction Construction Problem
题意:
给你个 (a) , (b) 让你求 (frac{c}{d} - frac{e}{f} = frac{a}{b})输出 a, b。且 (b gt d , b gt f)
如果 gcd(a, b) > 1
一定可以构造出一组解, 设 $x = frac{a} {gcd(a, b)}, y = frac{b}{gcd(a, b)} $
那么 (frac{x + 1}{y} - frac{1}{y} = frac{x}{y} = frac{a}{b})
如果 gcd(a,b) == 1
那么 可以得到 (frac{cf - ed}{df} = frac{a}{b})
-
如果 b只有一中质因子, 即 (b = p^k)(其中 p是质数), 那么一定无解。 证明 : 如果 (b = p^k)
那么可以得到 (df = p ^k) 可以得到 (d = p^{k - t}, f = p^{t}) 那么 (gcd(d, f) == k * p) 那么可以得到
a % (gcd(d, f)) != 0, 如果 a%gcd(d, f) == 0那么 gcd(a, b) != 1, 因为 gcd(a, b) = =1 所以成立。
又根据扩展gcd 可得无解。
-
如果 b == 1无解, 因为 a > d , a > f
-
其它情况均有解。可以构造 f = 一种质因数的乘积, d = b / f, 那么 gcd(f, d ) == 1
a % gcd(f, d) == 0所以可通过扩展gcd求得。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+7;
int prime[N],vis[N],top=1;
void Prime(){
for(int i=2;i<N;i++){
if(!vis[i])prime[top++]=i;
for(int j=1;j<top&&prime[j]*i<N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
ll a, b;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - (a / b) * y;
return d;
}
int ca[N];
int main() {
int t; scanf("%d", &t);
Prime();
for (int i = 1; i < top; i++) {
if (prime[i] > 2e6)break;
ca[prime[i]] = 1;
}
while (t--) {
scanf("%lld %lld", &b, &a);
ll g = __gcd(a, b);
if (g != 1) {
a = a / g;
b = b / g;
printf("%lld %lld %lld %lld
", b + 1, a, 1ll*1, a);
} else {
if (a == 1) {
printf("-1 -1 -1 -1
");
} else {
int f = 1;
if (ca[a]) f = 0;
if (f == 0) {
printf("-1 -1 -1 -1
");
} else {
ll d = 1;
ll f = 1;
ll pos = 2;
for (long long i = 2; i * i <= a; i++) {
if (a % i == 0) {
while(a % i == 0) d = d * i, a = a / i;
pos = i;
break;
}
}
if (a == 1) {
printf("-1 -1 -1 -1
");
} else {
for (ll i = pos; i * i <= a; i++) {
if (a % i == 0) {
while ( a % i == 0) f = f * i, a = a/ i;
}
}
if (a > 1) f = f * a;
ll x, y;
exgcd(d, f, x, y);
ll c = x * b;
ll e = y * b;
if (c < 0) {
printf("%lld %lld %lld %lld
", e, d, -c, f);
} else {
printf("%lld %lld %lld %lld
", c, f, -e, d);
}
}
}
}
}
}
}