Codeforces-Round-891-Div-3

Codeforces Round 891 (Div. 3)

A. Array Coloring

题目

给你一个由 n n n个整数组成的数组。您的任务是确定是否可以将其所有元素着色为两种颜色,使得两种颜色的元素之和具有相同的奇偶性,并且每种颜色至少有一个元素着色。

例如,如果数组为[ 1 , 2 , 4 , 3 , 2 , 3 , 5 , 4 1,2,4,3,2,3,5,4 1,2,4,3,2,3,5,4],我们可以将其着色为:[ 1 , 2 , 4 , 3 , 2 , 3 , 5 , 4 \color{blue}{1},\color{blue}{2},\color{red}{4},\color{blue}{3},\color{red}{2},\color{red}{3},\color{red}{5},\color{red}{4} 1,2,4,3,2,3,5,4],其中蓝色元素的总和为 6 6 6,红色元素的总和为【 18 18 18

思路

如果数组总和为奇数,那么显然分成的两组中必定有一组为奇数,另一组为偶数, 因为如果两组都是偶数或者奇数,那么总和一定是偶数。

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    int sum = 0;
    for (int i = 1; i <= n; i++) sum += a[i];
    if (sum % 2 == 0) yes else no

}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

B. Maximum Rounding

题目

给定一个自然数 x x x。您可以执行以下操作:

  • 选择一个正整数 k k k并将 x x x舍入到第 k k k位数字

请注意,位置从右到左编号,从零开始。如果数字有 k k k位,则认为第 k k k位的数字等于 0 0 0

舍入按如下方式完成:

  • 若第 ( k − 1 ) (k-1) (k1)位的数字大于或等于 5 5 5,则第 k k k位的数字增加 1 1 1,否则为 k k k位的数字-th 位置保持不变(使用数学舍入)。

  • 如果运算前第 k k k位的数字是 9 9 9,应该增加 1 1 1,那么我们就查找最小的位置 k ′ k' k k ′ > k k'>k k>k),其中的数字第 k ′ k' k位的数字小于 9 9 9,在第 k ′ k' k位的数字上加 1 1 1。然后我们分配 k = k ′ k=k' k=k

  • 之后,位置小于 k k k的所有数字都被替换为零。

如果您可以根据需要多次执行该操作,您的任务就是使 x x x尽可能大。

例如 x x x等于 3451 3451 3451,那么如果连续选择:

- k = 1 k=1 k=1,那么操作后 x x x将变成 3450 3450 3450
- k = 2 k=2 k=2,则操作后 x x x将变成 3500 3500 3500
- k = 3 k=3 k=3,那么操作后 x x x将变成 4000 4000 4000
- k = 4 k=4 k=4,那么操作后 x x x将变成 0 0 0

为了使答案最大化,您需要先选择 k = 2 k=2 k=2,然后选择 k = 3 k=3 k=3,那么数字就会变成 4000 4000 4000

思路

从左往右找第一个大于等于5的数,显然从这里可以开始往前进位是最优的,这时前面的数只要满足>=4就可以继续进位,因为5可以给它带来1,一直模拟直到不可以继续进位。

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    string x;
    cin >> x;
    int n = x.size();
    x = "0" + x;
    for (int i = 1; i <= n; i++) {
        if (x[i] >= '5') {
            int j = i - 1;
            while (j >= 0 && x[j] >= '4') j--;
            x[j]++;
            for (j = j+1; j <= n; j++) x[j] = '0';
            if (x[0] != '0') cout << x[0];
            for (j = 1; j <= n; j++) cout << x[j];
            cout << endl;
            return;
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << x[i];
    }
    cout << endl;


}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

C. Assembly via Minimums

题目

Sasha 有一个包含 n n n个整数的数组 a a a。他觉得无聊,对于 i i i j j j i < j i < j i<j),他写下了 a i a_i ai a j a_j aj的最小值。他获得了一个新的数组 b b b,大小为 n ⋅ ( n − 1 ) 2 \frac{n\cdot (n-1)}{2} 2n(n1)

例如,如果 a = a= a=[ 2 , 3 , 5 , 1 2,3,5,1 2,3,5,1],他会写[ min ⁡ ( 2 , 3 ) , min ⁡ ( 2 , 5 ) , min ⁡ ( 2 , 1 ) , min ⁡ ( 3 , 5 ) , min ⁡ ( 3 , 1 ) , m i n ( 5 , 1 ) \min(2, 3), \min(2, 5), \min(2, 1), \min(3, 5), \min(3, 1), min(5, 1) min(2,3),min(2,5),min(2,1),min(3,5),min(3,1),min(5,1)] = = =[ 2 , 2 , 1 , 3 , 1 , 1 2, 2, 1, 3, 1, 1 2,2,1,3,1,1]。

然后,他将数组 b b b的所有元素随机洗牌

不幸的是,他忘记了数组 a a a,而你的任务是恢复任何可能获得数组 b b b的数组 a a a

数组 a a a的元素应在 [ − 1 0 9 , 1 0 9 ] [-10^9,10^9] [109,109]范围内

思路

可以先开一个map来记录每个数字出现的次数

显然最小值一定会在b数组中出现n-1次,接下来是第2小的数一定会在b中出现n-2次,一直模拟下去即可。最后需要加一个比数组中最大的数还要大的数,因为这个数不会出现在b中,只要>=max(b[i])就行。

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    int n;
    cin >> n;
    int m = n * (n - 1) / 2;
    vector<int> a(n + 1), b(m + 1);
    for (int i = 1; i <= m; i++) cin >> b[i];
    map<int, int> mp;
    for (int i = 1; i <= m; i++) {
        mp[b[i]]++;
    }
    vector<int> res;
    int t = n - 1;
    for (auto [x, y]: mp) {
        while (y > 0) {
            y -= t;
            t--;
            res.push_back(x);
        }
    }
    res.push_back(res.back());
    for (auto i: res) cout << i << " ";
    cout << endl;
}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

D. Strong Vertices

题目

给定两个数组 a a a b b b,长度均为 n n n。两个数组的元素索引从 1 1 1 n n n。您正在构建一个有向图,如果 a u − a v ≥ b u − b v a_u-a_v \ge b_u-b_v auavbubv,则存在从 u u u v v v u ≠ v u\neq v u=v)的边。

如果存在从 V V V到所有其他顶点的路径,则顶点 V V V被称为强顶点。

有向图中的路径是由若干个顶点组成的链,通过边连接,从顶点 u u u出发,沿着边的方向,可以到达顶点 v v v

你的任务是找到所有强顶点。

例如,如果 a = [ 3 , 1 , 2 , 4 ] a=[3,1,2,4] a=[3,1,2,4] b = [ 4 , 3 , 2 , 1 ] b=[4,3,2,1] b=[4,3,2,1],图表将如下所示:

Codeforces-Round-891-Div-3_第1张图片

该图只有一个强顶点,编号为 4 4 4

思路

转换一下题目意思就容易了:

a u − a v > = b u − b v a_u-a_v>=b_u-b_v auav>=bubv可以变为: a u − b u > = a v − b v a_u-b_u>=a_v-b_v aubu>=avbv,

也就是满足题意的点一定需要满足对于其他所有顶点来说,它的 a [ i ] − b [ i ] a[i]-b[i] a[i]b[i]一定要大于其他的点,即最大值因此本题只需要统计 a [ i ] − b [ i ] a[i]-b[i] a[i]b[i]的最大值的数有多少个即可。

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1), b(n + 1), c(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i], c[i] = a[i] - b[i];
    int t = *std::max_element(c.begin() + 1, c.end());
    vector<int> res;
    for (int i = 1; i <= n; i++) if (c[i] == t) res.push_back(i);
    cout << res.size() << endl;
    for (int i = 0; i < res.size(); i++) cout << res[i] << " \n"[i == res.size() - 1];
}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

E. Power of Points

题目

给你 n n n个带有整数坐标 x 1 , … x n x_1,\dots x_n x1,xn的点,它们位于数轴上。

对于某个整数 s s s,我们构造线段[ s , x 1 s,x_1 s,x1]、[ s , x 2 s,x_2 s,x2]、 … \dots 、[ s , x n s,x_n s,xn]。请注意,如果是 x i < s x_ixi<s,那么该段将看起来像[ x i , s x_i,s xi,s]。线段[ a , b a, b a,b]覆盖了所有整数点 a , a + 1 , a + 2 , … , b a, a+1, a+2, \dots, b a,a+1,a+2,,b

我们将点 p p p的幂定义为与坐标 p p p的点相交的线段数,记为 f p f_p fp

你的任务是计算每个 s ∈ { x 1 , … , x n } s \in \{x_1,\dots,x_n\} s{x1,,xn} ∑ p = 1 1 0 9 f p \sum\limits_{p=1}^{10^9}f_p p=1109fp,即 1 1 1 1 0 9 10^9 109所有整数点的 f p f_p fp之和。

例如,如果初始坐标为 [ 1 , 2 , 5 , 7 , 1 ] [1,2,5,7,1] [1,2,5,7,1],我们选择 s = 5 s=5 s=5,那么线段将是: [ 1 , 5 ] [1,5] [1,5] [ 2 , 5 ] [2,5] [2,5] [ 5 , 5 ] [5,5] [5,5] [ 5 , 7 ] [5,7] [5,7] [ 1 , 5 ] [1,5] [1,5]。各点的幂为: f 1 = 2 , f 2 = 3 , f 3 = 3 , f 4 = 3 , f 5 = 5 , f 6 = 1 , f 7 = 1 , f 8 = 0 , … , f 1 0 9 = 0 f_1=2, f_2=3, f_3=3, f_4=3, f_5=5, f_6=1, f_7=1, f_8=0, \dots, f_{10^9}=0 f1=2,f2=3,f3=3,f4=3,f5=5,f6=1,f7=1,f8=0,,f109=0。他们的总和是 2 + 3 + 3 + 3 + 5 + 1 + 1 = 18 2+3+3+3+5+1+1=18 2+3+3+3+5+1+1=18

思路

因为点的顺序不影响答案,可以先对x数组排序,例如样例里面的

1 1 2 5 7

现在计算5这个点:

如果按照题目里面的计算方式太复杂,可以考虑计算长度,例如区间 [ 1 , 5 ] [1,5] [1,5]就是把区间1-5的点全部加1,最后再统计每个点的权值,

可以转换为计算这个区间的长度即可。

Codeforces-Round-891-Div-3_第2张图片

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<pair<int, int>> a(n + 1);
    vector<int> pre(n + 10), suf(n + 10);
    for (int i = 1; i <= n; i++) cin >> a[i].first, a[i].second = i;
    std::sort(a.begin() + 1, a.end());
    for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + a[i].first;
    for (int i = n; i >= 1; i--) suf[i] = suf[i + 1] + a[i].first;
    vector<int> res(n + 10);
    for (int i = 1; i <= n; i++) {
        int t = 1;
        t += a[i].first * (i - 1) + (i - 1) - pre[i - 1];
        t += suf[i + 1] + (n - i) - a[i].first * (n - i);
        res[a[i].second] = t;
    }
    for (int i = 1; i <= n; i++) cout << res[i] << " \n"[i == n];


}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

F. Sum and Product

题目

您有一个长度为 n n n的数组 a a a

你的任务是回答 q q q个查询:给定 x , y x,y x,y,找到 a i + a j = x a_i + a_j = x ai+aj=x a i ⋅ a j = y a_i \cdot a_j = y aiaj=y同时存在的 i i i j j j 1 < = i < j < = n 1 <= i < j <= n 1<=i<j<=n)对的数量。

也就是说,对于数组 [ 1 , 3 , 2 ] [1,3,2] [1,3,2]并要求 x = 3 , y = 2 x=3,y=2 x=3,y=2,答案是 1 1 1

  • i = 1 i=1 i=1 j = 2 j=2 j=2失败,因为 1 + 3 = 4 1 + 3 = 4 1+3=4而不是 3 , 3, 3,,也是 1 ⋅ 3 = 3 1 \cdot 3=3 13=3而不是 2 2 2
  • i = 1 i=1 i=1 j = 3 j=3 j=3满足两个条件;
  • i = 2 i=2 i=2 j = 3 j=3 j=3失败,因为 3 + 2 = 5 3 + 2 = 5 3+2=5而不是 3 , 3, 3,,也是 3 ⋅ 2 = 6 3 \cdot 2=6 32=6而不是 2 2 2

思路

可以先用map记录每个数字出现的次数

a [ i ] + a [ j ] = x a[i]+a[j]=x a[i]+a[j]=x

a [ i ] ∗ a [ j ] = y a[i]*a[j]=y a[i]a[j]=y

可以把 a [ i ] , a [ j ] a[i],a[j] a[i],a[j]看成是方程 t 2 − x t + y = 0 t^2-xt+y=0 t2xt+y=0的两个解,这一步可以使用韦达定理,或者直接把 a [ i ] = y / a [ j ] a[i]=y/a[j] a[i]=y/a[j]带入第一个式子计算得到

韦达定理: a x 2 + b x + c = 0 ax^2+bx+c=0 ax2+bx+c=0如果有解,则满足

x 1 + x 2 = − b a x_1+x_2=-\frac{b}{a} x1+x2=ab

x 1 ∗ x 2 = c a x_1*x_2=\frac{c}{a} x1x2=ac

于是题目就转换为求解满足方程的解有多少组

d e l t a = b 2 − 4 a c = x 2 − 4 y delta=b^2-4ac=x^2-4y delta=b24ac=x24y

  • 如果 d e l t a < 0 delta<0 delta<0方程无解

  • 如果 d e l t a = 0 delta=0 delta=0,方程有两个相同的解 x 1 = x 2 x1=x2 x1=x2,此时只需要从map中选择两个 = x 1 =x_1 =x1的数,即 C c n t 2 C_{cnt}^2 Ccnt2(cnt为=x1的个数)

  • 如果 d e l t a > 0 delta>0 delta>0,需要判断是否有两个不同的整数解, x = x + − d e l t a 2 x=\frac{x+-\sqrt{delta}}{2} x=2x+−delta ,判断是否为整数,为答案的贡献为 c n t 1 ∗ c n t 2 cnt1*cnt2 cnt1cnt2

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    std::sort(a.begin() + 1, a.end());
    map<int, int> mp;
    for (int i = 1; i <= n; i++) mp[a[i]]++;
    int q;
    cin >> q;
    while (q--) {
        int x, y;
        cin >> x >> y;
        int delta = x * x - 4 * y;
        if (delta < 0) {
            cout << 0 << endl;
        } else if (delta == 0) {
            cout << mp[x / 2] * (mp[x / 2] - 1) / 2 << " ";
        } else {
            int sq = sqrt(delta);
            if (sq * sq != delta) {
                cout << 0 << " ";
            } else {
                if ((x + sq) % 2 != 0 || (x - sq) % 2 != 0) {
                    cout << 0 << " ";
                } else {
                    int x1 = (x + sq) / 2;
                    int x2 = (x - sq) / 2;
                    cout << mp[x1] * mp[x2] << " ";
                }
            }
        }
    }
    cout << endl;


}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

G. Counting Graphs

题目

给定一棵由 n n n个顶点组成的树。树是无环的连通无向图。树的每条边都有其重量, w i w_i wi

您的任务是计算满足所有四个条件的不同图表的数量:

1、图不存在自环和重边。
2、图边的权重为整数且不超过 S S S
3. 该图恰好有一个 最小生成树。
4. 图的最小生成树是给定的树。

如果两个图的边集不同,则考虑到边的权重,则认为两个图不同。

答案可以很大,输出对 998244353 998244353 998244353取模。

思路

假设现在已经有了一个最小生成树,我们现在考虑要往上面进行加边操作,显然这条加的边一定是要比两个点 x , y x,y x,y之前的权值要大,否则最小生成树就可以选择刚刚加的这条边,那么最小生成树就要变了。因为边的权值上限为 S S S,因此加的边的取值范围为 w ~ S w~S wS,即有 S − w + 1 S-w+1 Sw+1中选择

模拟克鲁思卡尔算法求最小生成树的过程,对于两个点 x , y x,y x,y,如果他们不在同一个集合,假设左边 x x x集合的大小为 s 1 s_1 s1, y y y集合的大小为 s 2 s_2 s2,显然我可以从 x x x里面任意选一个点,从 y y y里面任意选一个点,然后这两个点之间可以加任意一条 w ~ S w~S wS的边,都不会影响到最小生成树。

选点的组合有 s 1 ∗ s 2 − 1 s1*s2-1 s1s21个组合,,每个组合都可以选择 S − w + 1 S-w+1 Sw+1个边,方案数为: ( S − w + 1 ) s 1 ∗ s 2 − 1 (S-w+1)^{s1*s2-1} (Sw+1)s1s21

S1*s2-1的减1是因为x和y这两个点是最小生成树的点,他们之间已经有一条边为w了,不可以再加边了,否则就是重边了

代码

#include 

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

const int mod = 998244353;

struct DSU {
    std::vector<int> f, siz;

    DSU(int n) {
        f.resize(n + 1);
        siz.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            f[i] = i;
            siz[i] = 1;
        }
    }

    int find(int x) {
        if (x != f[x]) f[x] = find(f[x]);
        return f[x];
    }

    bool same(int x, int y) {
        return find(x) == find(y);
    }

    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }

    int size(int x) {
        return siz[find(x)];
    }
};

int qmi(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

void solve() {
    int n, s;
    cin >> n >> s;
    vector<array<int, 3>> edge(n - 1);
    for (int i = 0; i < n - 1; i++) cin >> edge[i][0] >> edge[i][1] >> edge[i][2];
    sort(edge.begin(), edge.end(), [](array<int, 3> a, array<int, 3> b) { return a[2] < b[2]; });
    DSU dsu(n);
    int res = 1;
    for (auto [x, y, w]: edge) {
        int s1 = dsu.size(x), s2 = dsu.size(y);
        res = (res * qmi(s + 1 - w, s1 * s2 - 1)) % mod;
        dsu.merge(x, y);
    }
    cout << res << endl;
}

signed main() {
    IOS
#ifndef ONLINE_JUDGE
    freopen("../test.in", "r", stdin);
    freopen("../test.out", "w", stdout);
#endif
    int _ = 1;
    cin >> _;
    while (_--) solve();
    return 0;
}

你可能感兴趣的:(ACM-,ICPC,#,Codeforces,算法,数据结构,c++)