Codeforces Round 891 (Div. 3)
Codeforces Round 881 (Div. 3)
A.Array Coloring
题目大意
思路
简单判断数组和的奇偶性即可(小学数学)
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int sum = 0, x;
for (int i = 1; i <= n; i++)
{
cin >> x;
sum += x;
}
if (sum % 2 == 0)
puts("YES");
else
puts("NO");
}
return 0;
}
Maximum Rounding
题目大意
遍历一个数字的每一位,判断在哪一位四舍五入值最大。
思路
按照题意模拟即可(比赛时被 HACK 了)
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
string s;
cin >> s;
s = '0' + s;
int lst = s.size() - 1;
for (int i = s.size() - 1; i >= 0; i--)
{
if (s[i] >= '5')
{
for (int j = i; j <= lst; j++)
s[j] = '0';
lst = i - 1;
if (s[i - 1] != '9')
s[i - 1] += 1;
else
{
while (s[i - 1] != '9')
s[i - 1] = '0', i--;
s[i - 1]++;
}
}
}
if (s[0] != '0')
cout << s << endl;
else
cout << s.substr(1) << endl;
}
return 0;
}
C.Assembly via Minimums
题目大意

思路
假设 \(a\) 有序,对 \(b\) 也进行排序,这是不影响结果的。
设 \(a\) 中最小值为 \(x\),则 \(x\) 在排序后的 \(b\) 中会连续出现 \(n-1\) 次,设次小值为 \(x_1\) ,在排序后的 \(i\) 会出现 \(n-2\) 次(除了 \(x\) 其他都是本身) ,以此类推,第 \(n-1\) 个数为 \(x\),第 \(n-1+n-2\) 个数为 \(x_1\) ...
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL a[N], b[N];
int n;
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
for (int i = 1; i <= n * (n - 1) / 2; i++)
cin >> a[i];
sort(a + 1, a + n * (n - 1) / 2 + 1);
int cnt = 0, idx = 0;
for (int i = n - 1; i; i--)
{
cnt += i;
b[++idx] = a[cnt];
}
for (int i = 1; i < n; i++)
cout << b[i] << ' ';
cout << b[n - 1] << endl;
}
return 0;
}
D - Strong Vertices
题目大意

思路
将题目中的式移向得
当 \(a_u-b_u\) 最大时才会向其他所有顶点连边,所以只需要找最大 \(a_u-b_u\) 即可
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
struct node
{
LL id, num;
} a[N];
bool cmp(node a, node b)
{
return a.id < b.id;
}
bool cmp2(node a, node b)
{
return a.num > b.num;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
LL maxx = -0x3f3f3f3f3f3f;
for (int i = 1; i <= n; i++)
cin >> a[i].num;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
a[i].num -= x;
a[i].id = i;
maxx = max(maxx, a[i].num);
}
sort(a + 1, a + n + 1, cmp2);
int ans = 0;
for (int i = 1; i <= n; i++)
ans += (a[i].num == maxx);
cout << ans << endl;
sort(a + 1, ans + a + 1, cmp);
for (int i = 1; i <= ans; i++)
cout << a[i].id << ' ';
cout << endl;
}
return 0;
}
E - Power of Points
题目大意
给定 \(n\) 个数轴上的点,你需要依次选中每个点,计算选中该点的权值。
选中一个点后,将它和每一个点之间连一条线段(包括它自己),线段会覆盖两端点之间的整点一次(包括两个端点),权值定义为数轴上每个整点被覆盖的次数的和。
依次选中 \(1\) 到 \(n\) 的每个点,计算权值并输出。
思路
题目给我们的是 被覆盖的次数和 实际上就是求线段的总长度
对于一条线段 \([l,r]\) 如果 \(l<r\) 那么长度为 \(r-l+1\),反之为 \(l-r+1\),而 \(l,r\) 分别为 \(x_i,s\)
所以易得线段总长为 \(\sum_{i=1}^n(|x_i-s|+1)=\sum_{i=1}^n(|x_i-s|)+n\)
问题来到如何快速求解以上式子,我们其实可以先拆分
把式子分成两部分
- \((x_i-x_1)+(x_i-x_2)+...(x_i-x_i)\) 这个部分可以化为 \(i\times x_i-\sum_{j=1}^{i}x_j\) 这个不难维护,为前缀和。
- \((x_{i+1}-x_i)+...+(x_n-x_i)\) 这个部分可以化为 \(\sum_{j=i+1}^{n}x_j-(n-i)\times x_i\) 这个就是后缀和维护
然后将两个式子拼起来即可。
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].num, a[i].id = i;
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++)
f[i] = a[i].num + f[i - 1];
h[n + 1] = 0;
for (int i = n; i >= 1; i--)
h[i] = h[i + 1] + a[i].num;
for (int i = 1; i <= n; i++)
a[i].ans = a[i].num * (i - 1) - f[i - 1] + h[i + 1] - a[i].num * (n - i) + n;
sort(a + 1, a + n + 1, cmp2);
for (int i = 1; i <= n; i++)
cout << a[i].ans << ' ';
cout << endl;
}
F - Sum and Product
题目大意
思路
考虑 \(a_j=x-a_i\) ,带入积的那个式子就是 \((x-a_i)\times a_i=y\) 也就是 \((x\times a_i-a_{i}^2=y)\)
移向可得 \(a_i^2-x \times a_i+y=0\) ,这样就变成一个一元二次方程的一般形式了
运用公式得 \(a_i=\frac{x±\sqrt{x^2-4y}}{2}\) 不难想到 \(\sqrt{x^2-4y}\) 小于 \(0\) 时无解,若得出来的两个解的和不为 \(x\) ,积不为 \(y\) 那么也输出 \(0\)
算可能性易得用乘法原理,为 \(cnt_{a_i} \times cnt_{a_j}\) cnt表示值得出现次数
若 \(a_i=a_j\) 为 \(C_{2}^{a_i}\) 为 $\frac{cnt_{a_i} \times cnt_{a_i-1}}{2} $
LL solve(LL b, LL c)
{
LL a1, a2;
LL delta = b * b - 4 * c;
if (delta < 0)
return 0;
a1 = (b - sqrt(delta)) / 2;
a2 = (b + sqrt(delta)) / 2;
if (a1 + a2 != b || a1 * a2 != c)
return 0;
if (delta == 0)
return (LL)mp[a1] * (mp[a1] - 1) / 2;
return (LL)mp[a1] * mp[a2];
}
G题
这道题首先我们肯定就是要添加不是最小生成树上的边
既然是添加,那么就一定有一些条件
1.边权大于最小生成树的树边的最大值
原因即为:
如果添加的边等于树边的最大值,那么最小生成树不唯一
如果小于,那么最小生成树就不是给定的那棵
然后考虑所有满足条件的图的数量
对于两个集合 \(a,b\) 其中 \(a \to b\) 是有一条边的,那么考虑将一个集合上的点跑到另外一个集合上的点去
那么显然这个跑的路径是大于树边最大值,而且这些边的总数显然为 \(siz_a*siz_b-1\) ,其中 \(siz\) 的
意思为某个集合的节点数量, \(-1\) 表示 \(a \to b\)这条边不能连,而这些边权大小范围显然是 \(s-w+1\) , \(w\)表示树边的权值,原来是 \([w+1,s]\) 中的个数,但是还有一个不加边,所以加上1
最大值,那么两个集合之间的可能性易得:\(s-w+1^{siz_a \times siz_b-1}\)
这个可能不太好理解
我们举个例子,比如有n棵骰子,每个骰子落地后朝上的那一面大小范围为 \(1\sim6\),那么这些骰子一起掉落
可能性为 \((6^n)\)
这其实就是多个乘法原理的组合
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 998244353;
ll fa[N]; //并查集
ll siz[N];
int n, s;
struct node
{
int u, v, w;
} a[N];
ll find(ll x) //并查集查找
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
bool cmp(node a, node b)
{
return a.w < b.w;
}
ll qpow(ll a, ll k) //快速幂
{
ll res = 1;
while (k)
{
if (k & 1)
res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res;
}
void solve()
{
cin >> n >> s;
for (int i = 1; i <= n; i++)
fa[i] = i, siz[i] = 1;
for (int i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
a[i] = {u, v, w};
}
sort(a + 1, a + n, cmp);
ll ans = 1;
for (int i = 1; i < n; i++)
{
int fx = find(a[i].u), fy = find(a[i].v);
ans = ans * qpow(s - a[i].w + 1, siz[fx] * siz[fy] - 1) % mod;
fa[fx] = fy;
siz[fy] += siz[fx];
}
cout << ans % mod << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}

浙公网安备 33010602011771号