Codeforces Round 891 (Div. 3)

Codeforces Round 881 (Div. 3)

A.Array Coloring

题目大意

link

思路

简单判断数组和的奇偶性即可(小学数学)

#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

题目大意

![image-20230808200812873](C:\Users\Jianfan Li\AppData\Roaming\Typora\typora-user-images\image-20230808200812873.png)

思路

假设 \(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

题目大意

![image-20230808201625409](C:\Users\Jianfan Li\AppData\Roaming\Typora\typora-user-images\image-20230808201625409.png)

思路

将题目中的式移向得

\[a_u-b_u≥b_u-b_v \]

\(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+1}-x_i)+...+(x_n-x_i)+n \]

把式子分成两部分

  1. \((x_i-x_1)+(x_i-x_2)+...(x_i-x_i)\) 这个部分可以化为 \(i\times x_i-\sum_{j=1}^{i}x_j\) 这个不难维护,为前缀和。
  2. \((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

题目大意

link

思路

考虑 \(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;
}
posted @ 2023-08-08 20:24  ljfyyds  阅读(46)  评论(0)    收藏  举报