Codeforces Round #665 (Div. 2) A-E
Codeforces Round #665 (Div. 2)
A Distance and Axis
题意
在X正半轴上有点 \(A=n\),我们想找到一个整数点 B(也在OX轴上),使得 \(OB\) 的距离减 \(AB\) 的距离的绝对值等于 \(k\) ,如果B找不到,可以将A点每次左移或右移一个单位,知道找到一个B点。求A的最少移动次数。
Solution
如果k大于n,那么B一定在A右边,就变成了 \(OB-AB=OA\) 的距离是k,就把A移动到k位置。
如果k小于等于n,假设B的坐标是m,则 \(|(n-m)-m|=k\),整理一下并且把绝对值去掉,就变成了了 \(2m=n-k\) 或者 \(2m=n+k\),因为m是整数,所以就能得到结论n和k奇偶性相同,答案只能是0或1。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int M = 1e6 + 5;
const int N = 1e5 + 5;
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
while (t--)
{
int n, k;
cin >> n >> k;
if (n >= k)
cout << ((n - k) % 2) << endl;
else
cout << k - n << endl;
}
return 0;
}
B Ternary Sequence
题意
给定两个长度为 \(n\) 的序列 \(A,B\),他们中分别由 \(x_i,y_i,z_i\) 个 \(0,1,2\) 构成,你可以重新排列A和B中的元素顺序,使得 \(\sum c_i\) 最大。
Solution
贪心。A、B中的0、1、2两两匹配有9种可能,可以得到(1,2)等于-2,(2,1)等于2,其他组合都等于0。
所以要使(2,1)组合尽可能多,使(1,2)组合尽可能少。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int M = 1e6 + 5;
const int N = 1e5 + 5;
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
cin >> t;
while (t--)
{
int a[3], b[3];
for (int i = 0; i < 3; i++)
cin >> a[i];
for (int i = 0; i < 3; i++)
cin >> b[i];
int ans = 0;
int temp = min(a[2], b[1]); // (ai=2, bi=1) 尽可能多
ans += temp * 2;
a[2] -= temp;
b[1] -= temp;
if (a[1] > b[0] + b[1]) // 先不匹配 (ai=1, bi=2) 的组合,先尽可能把其他组合匹配完
ans -= (a[1] - b[0] - b[1]) * 2;
cout << ans << endl;
}
return 0;
}
C Mere Array
题意
给你一个长度为 \(n\) 的数组 \(a\) ,\(minn\) 是数组 \(a\) 的最小值,当 \(gcd(a_i,a_j)==minn\) 时,可以交换 \(a_i,a_j\) 的位置,问能否通过交换使得数组 \(a\) 变成非递减的。
Solution
思维题。分析一下,因为 \(gcd(a_i,a_j)==minn\) ,所以 \(gcd(a_i,minn)==minn\) ,\(gcd(a_j,minn)==minn\) ,那么要交换 \(a_i,a_j\) 就能通过 \(minn\) 作为中间值把两个数交换。
那么我们考虑哪些数需要交换位置?假设数组 \(b\) 是排好序的,如果 \(a_i\ne b_i\) ,那么说明 \(a_i\) 需要交换位置,如果 \(a_i\) 不能整除 \(minn\) ,那么就说明 \(a_i\) 永远不能换位置,否则 \(a_i\) 就能通过 \(minn\) 作为中间交换变量到达它应该在的位置,可以考虑最简单的冒泡来证明。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int M = 1e6 + 5;
const int N = 1e5 + 5;
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
vector<int> a(n);
int minn = INF;
for (int i = 0; i < n; i++)
{
cin >> a[i];
minn = min(a[i], minn);
}
vector<int> b(a);
sort(b.begin(), b.end());
bool flag = true;
for (int i = 0; i < n; i++)
{
if (a[i] != b[i] && a[i] % minn)
{
flag = false;
break;
}
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
D Maximum Distributed Tree
题意
给定一棵 \(n\) 个结点的树,给这棵树的边分配边权,使得所有边权的乘积等于 \(k\) ,\(k\) 以 \(m\) 个质因子的形式给出,并且要求 $$\sum_{i=1}{n-1}\sum_{j=i+1}f(i,j)$$ 最大,其中 \(f(i,j)\) 表示 \(i\) 到 \(j\) 的简单路径边权之和。
Solution
考虑每条边对答案的贡献。我们想一下,一条边可能出现在哪些简单路径中,答案显然是以这条边分开的上下两子树中任意结点组合的简单路径,我们记数组 \(sz[i]\) 表示 \(i\) 结点的子树大小(包括 \(i\) 结点),那么 \(i\) 到它的父亲这条边对答案的贡献次数就是 \(sz[i]\times (n-sz[i])\) ,统计出每条边的贡献次数 \(q\) ,然后我们贪心地把最大的质因子 \(p\) ,分配给贡献次数最多的边即可。
这里要区分 \(m\le n-1\) 和 \(m>n-1\) 这两种情况,第一种是边多质因子不够分的,只能补1,第二种是边少质因子多,我们要把多出来的几个最大的质因子相乘变成一个,这样才使最大化。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
const int M = 1e6 + 5;
const int N = 1e5 + 5;
vector<int> e[N];
ll sz[N], p[N], q[N];
void dfs(int u, int fa)
{
sz[u] = 1;
for (int v : e[u])
{
if (v == fa)
continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
for (int i = 0; i <= n; i++)
e[i].clear(), sz[i] = 0, p[i] = 1; // 质因子要初始化为 1
for (int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1, 0); // dfs求各子树总节点个数
int m;
cin >> m;
for (int i = 1; i <= m; i++) // 输入 K 的质因子
cin >> p[i];
if (m < n - 1) // 如果质因子个数 m 不够 n-1 条边,补 1
m = n - 1;
sort(p + 1, p + m + 1);
for (int i = n; i <= m; i++) // 如果质因子个数 m 超过了 n-1 则将后面的质因子乘到第 n-1 个质因子上,保证 K 的大小
p[n - 1] = (p[n - 1] * p[i]) % MOD;
for (int i = 1; i < n; i++) // 1号节点是dfs的根节点,无与父亲节点的连边,所以求剩下的 n-1 个点与其父亲节点的连边对答案的贡献次数
q[i] = sz[i + 1] * (n - sz[i + 1]); // 不能取模,下面要对 q[] 排序,而且也没有超longlong
sort(q + 1, q + n);
ll ans = 0;
for (int i = 1; i < n; i++)
ans = (ans + ((p[i] % MOD) * (q[i] % MOD)) % MOD) % MOD;
cout << ans << endl;
}
return 0;
}
E Divide Square
题意
在平面直角坐标系中 \((0,0)\),\((0,16^6)\),\((10^6,0)\),\((10^6,10^6)\) 这四个点围成的正方形区域中,有 \(n\) 条水平线段和 \(m\) 条竖直线段,并且所有的线段至少与正方形的一侧相交,保证在同一条直线上没有两条线段,问这些线段把这个大正方形分成了多少块。
Solution
线段树 / 树状数组。
在给定的条件下,只有两种情况会导致块数增加。
- 水平线和竖直线相交会增加一块。
- 一条线段的两端都与大正方形相交会增加一块。
加上原来的大正方形是一块。
求线段相交的次数,可以用平行于 \(y\) 轴的直线从左到右扫描。具体方法:直线平行于 \(y\) 轴,所以树状数组是记录 \(y\) 轴上每个点的个数,遇到水平线段的左端点就加一,遇到水平线段的右端点就减一,这样就能知道当竖直直线扫描的这个位置时,有多少条横向线段与之相交。
这里需要注意的是,树状数组计算时是从1开始的,而题目中的坐标是从0到106,所以在计算时要把坐标加一。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1e9 + 7;
const int M = 1e6 + 5;
const int N = 1e5 + 5;
int n, m;
ll c[M]; // Y坐标的树状数组,注意坐标范围 1e6
struct Hnode // 横线的左右端点
{
int x, y, d; // d = 1 左端点加一 d = -1 右端点减一
bool operator<(const Hnode &OTHER) const { return x < OTHER.x; }
} a[N << 1];
struct Snode // 竖线
{
int x, ly, ry;
bool operator<(const Snode &OTHER) const { return x < OTHER.x; }
} b[N];
int lowbit(int x) { return x & (-x); }
void add(int x, int d)
{
while (x < M)
{
c[x] += d;
x += lowbit(x);
}
}
ll query(int x)
{
ll res = 0;
while (x)
{
res += c[x];
x -= lowbit(x);
}
return res;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
int cnt = 0;
ll ans = 1; // 开始是一整块
for (int i = 1; i <= n; i++) // 横线
{
int y, lx, rx;
cin >> y >> lx >> rx;
a[++cnt] = {lx, y, 1};
a[++cnt] = {rx + 1, y, -1};
if (lx == 0 && rx == 1000000)
ans++;
}
for (int i = 1; i <= m; i++) // 竖线
{
cin >> b[i].x >> b[i].ly >> b[i].ry;
if (b[i].ly == 0 && b[i].ry == 1000000)
ans++;
}
sort(a + 1, a + cnt + 1);
sort(b + 1, b + m + 1);
for (int i = 1, j = 0; i <= m; i++)
{
while (j < n * 2 && a[j + 1].x <= b[i].x)
{
j++;
add(a[j].y + 1, a[j].d); // 树状数组是从下标 1 开始的,题目中坐标从 0 开始,所以这里要把对应坐标加 1
}
ans += query(b[i].ry + 1) - query(b[i].ly);
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号