Codeforces Round 947 (Div. 1 + Div. 2) A~F

A.Bazoka and Mocha’s Array(思维)

题意:

摩卡喜欢数组,所以在出发前,巴祖卡送给她一个由 n n n个正整数组成的数组 a a a作为礼物。

现在,摩卡想知道数组 a a a在执行下面的操作若干次(可能是零次)后,能否以不递减的顺序排序:

  • 将数组分成前缀和后缀两部分,然后交换这两部分。换句话说,让 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x a:=y+x a:=y+x。这里 + + +表示数组连接操作。

例如,如果是 a = [ 3 , 1 , 4 , 1 , 5 ] a=[3,1,4,1,5] a=[3,1,4,1,5],我们可以选择 x = [ 3 , 1 ] x=[3,1] x=[3,1] y = [ 4 , 1 , 5 ] y=[4,1,5] y=[4,1,5],满足 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x = [ 4 , 1 , 5 , 3 , 1 ] a:=y+x=[4,1,5,3,1] a:=y+x=[4,1,5,3,1]。我们还可以选择 x = [ 3 , 1 , 4 , 1 , 5 ] x=[3,1,4,1,5] x=[3,1,4,1,5] y = [   ] y=[\,] y=[],满足 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x = [ 3 , 1 , 4 , 1 , 5 ] a:=y+x=[3,1,4,1,5] a:=y+x=[3,1,4,1,5]。注意,我们不能选择 x = [ 3 , 1 , 1 ] x=[3,1,1] x=[3,1,1] y = [ 4 , 5 ] y=[4,5] y=[4,5],也不能选择 x = [ 1 , 3 ] x=[1,3] x=[1,3] y = [ 5 , 1 , 4 ] y=[5,1,4] y=[5,1,4],因为这两个选项都不满足 a = x + y a=x+y a=x+y

分析:

x + y → y + x x+y→y+x x+yy+x的操作其实就是把数组开头的一段移动到末尾。如果操作大于一次,可以合并成一个操作。因此最多操作一次。直接复制两边进行判定即可。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        vector<int> a(n + n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            a[i + n] = a[i];
        }
        bool flag = 0;
        for (int i = 0; i < n; i++) {
            bool fl = 1;
            for (int j = i; j + 1 < i + n; j++) {
                fl &= (a[j] <= a[j + 1]);
            }
            flag |= fl;
        }
        if (flag) {
            cout << "Yes" << endl;
        } else {
            cout << "No" << endl;
        }
    }
    return 0;
}

B.378QAQ and Mocha’s Array(思维)

题意:

摩卡喜欢数组,所以在出发前,378QAQ送给她一个由 n n n个正整数组成的数组 a a a作为礼物。

摩卡认为,如果存在两个数 i i i j j j 1 ≤ i , j ≤ n 1\leq i,j\leq n 1i,jn i ≠ j i\neq j i=j),使得对于所有的 k k k 1 ≤ k ≤ n 1\leq k\leq n 1kn), a k a_k ak都能被 a i a_i ai a j a_j aj整除 † ^\dagger ,那么 a a a就是美丽的。

判断 a a a是否美丽。

† ^\dagger 如果存在整数 z z z使得 x = y ⋅ z x=y\cdot z x=yz,那么 x x x可以被 y y y整除。

分析:

首先,最小的 a i a_i ai i i i一定要选,否则后面无法补偿。

因此我们已经确定了一个了。那么,我们把 m o d    a i = 0 \mod a_i=0 modai=0的全部去掉,最小的就是另一个答案。最后再判断一次。

代码:

#include <bits/stdc++.h>
using namespace std;
int dp[10005];
string a;
long long mod=1e9+7;
int main(){
    int n;
    cin>>n;
    cin>>a;
    a='#'+a;
    dp[0]=0;
    dp[1]=1;
    for(int i=2;i<=n;i++){
        if((a[i-1]-'0')*10+(a[i]-'0')<=25&&(a[i-1]-'0')*10+(a[i]-'0')>=10){
            dp[i]=(dp[i-1]+dp[i-2])%mod;
        }else{
            dp[i]=dp[i-1]%mod;
        }
    }
    cout<<dp[n];
    return 0;
}

C.Chamo and Mocha’s Array(区间)

题意:

摩卡喜欢数组,所以在出发前,查莫送给她一个由 n n n个正整数组成的数组 a a a作为礼物。

摩卡不喜欢包含不同数字的数组,所以摩卡决定用魔法来改变数组。摩卡可以执行下面的三步操作若干次(可能是零次):

  1. 选择索引 l l l r r r( 1 ≤ l < r ≤ n 1\leq l\lt r\leq n 1l<rn)
  2. x x x成为子数组 [ a l , a l + 1 , … , a r ] [a_l,a_{l+1},\ldots,a_r] [al,al+1,,ar]的中位数 † ^\dagger
  3. 设置所有值 a l , a l + 1 , … , a r a_l,a_{l+1},\ldots,a_r al,al+1,,ar x x x

假设初始值为 a = [ 1 , 2 , 3 , 4 , 5 ] a=[1,2,3,4,5] a=[1,2,3,4,5]

  • 如果摩卡在第一次操作中选择了 ( l , r ) = ( 3 , 4 ) (l,r)=(3,4) (l,r)=(3,4),那么 x = 3 x=3 x=3这个数组就会变成 a = [ 1 , 2 , 3 , 3 , 5 ] a=[1,2,3,3,5] a=[1,2,3,3,5]
  • 如果摩卡在第一次操作中选择了 ( l , r ) = ( 1 , 3 ) (l,r)=(1,3) (l,r)=(1,3),那么 x = 2 x=2 x=2,数组将变为 a = [ 2 , 2 , 2 , 4 , 5 ] a=[2,2,2,4,5] a=[2,2,2,4,5]

摩卡会一直执行操作,直到数组只包含相同的数字。摩卡想知道这个数字的最大可能值是多少。

† ^\dagger 在长度为 m m m的数组 b b b中,中位数是按非递减顺序对元素排序后占据位置编号 ⌊ m + 1 2 ⌋ \lfloor\frac{m+1}{2}\rfloor 2m+1的元素。例如, [ 3 , 1 , 4 , 1 , 5 ] [3,1,4,1,5] [3,1,4,1,5]的中位数是 3 3 3 [ 5 , 25 , 20 , 24 ] [5,25,20,24] [5,25,20,24]的中位数是 20 20 20

分析:

考虑到如果有两个相同的连续元素了,一定可以通过操作把这个序列全部变为这个元素。

怎么出现两个相同元素呢?容易发现操作 > 3 \gt 3 >3的长度的区间是不优的,一定可以拆成更多的小区间。只需要判断 2 , 3 2,3 2,3长度的即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 1e5 + 10;

int a[N];

void solve() {
    int n;
    cin >> n;
    a[0] = a[n + 1] = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int mx = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i - 1] >= a[i] || a[i + 1] >= a[i]) {
            mx = max(mx, a[i]);
        }
        if (i >= 3) {
            if (a[i - 2] >= a[i]) {
                mx = max(mx, a[i]);
            }
        }
        if (i + 2 <= n) {
            if (a[i + 2] >= a[i]) {
                mx = max(mx, a[i]);
            }
        }
    }
    cout << mx << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

D.Paint the Tree(搜索、贪心)

题意:

378QAQ有一棵有 n n n个顶点的树。初始时,所有顶点都是白色的。

树上有两颗棋子,分别叫做 P A P_A PA P B P_B PB P A P_A PA P B P_B PB最初分别位于顶点 a a a b b b。在一个步骤中,378QAQ将依次执行以下操作:

  1. P A P_A PA移动到邻近顶点。如果目标顶点是白色,那么这个顶点将被涂成红色。
  2. P B P_B PB移动到相邻的顶点。如果目标顶点为红色,则此顶点将被涂抹为蓝色。

最初,顶点 a a a会被涂成红色。如果是 a = b a=b a=b,顶点 a a a将被涂成蓝色。请注意,每一步都必须移动两个棋子。任何时候都可以有两个棋子位于同一个顶点上

378QAQ想知道将所有顶点都涂成蓝色所需的最少步数。

分析:

P A P_A PA P B P_B PB在一起,我们每次可以移动一下 P A P_A PA,然后 P B P_B PB移到同样的位置,这样就要移动 2 n − 2 − R 2n−2−R 2n2R,其中 R R R为离原来的点最远的节点的距离。

假设只需要 P B P_B PB,这个答案是显然的,有了 P A P_A PA,限制条件增加,答案不降,因此正确。

考虑稍特殊的情况,两棋子距离为偶数,为了第二个棋子尽快开始遍历染色,显然需要两个棋子快速走到一起。

考虑距离为奇数,显然首先仍然为快速接近。接着考虑怎么走才最贪心,可以红走一次,蓝跟到上一个红的地方,这样二者可相邻,且染色有效,显然要染色都要染成红色,但是最后一次蓝色少走一步,所以要再加上一。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 1000010;

LL n, a, b, ans;
struct st {
    LL v, ne;
} sd[N];
LL h[N];
LL inn;

void add(LL u, LL v) {
    sd[++inn].v = v;
    sd[inn].ne = h[u];
    h[u] = inn;
}

LL fa[N], d[N];

void dfs(LL t) {
    d[t] = d[fa[t]] + 1;
    for (LL i = h[t]; i; i = sd[i].ne) {
        if (sd[i].v != fa[t]) {
            fa[sd[i].v] = t;
            dfs(sd[i].v);
        }
    }
}

LL del(LL t, LL fa) {
    LL res = -1;
    for (LL i = h[t]; i; i = sd[i].ne) {
        if (sd[i].v != fa) {
            res = max(res, del(sd[i].v, t));
        }
    }
    return res + 1;
}

void cal() {
    inn += 10;
    while (inn) {
        sd[inn--] = {0, 0};
    }
    for (LL i = 0; i <= n + 10; i++) {
        h[i] = fa[i] = d[i] = 0;
    }
    ans = 0;
}

void solve() {
    cin >> n;
    cin >> a >> b;
    fa[a] = a;
    for (LL i = 1; i < n; i++) {
        LL u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    fa[a] = 0;
    d[0] = -1;
    dfs(a);
    ans += d[b] / 2;
    ans += 2 * n - 2;
    LL z;
    z = d[b];
    for (LL i = 1; i <= z / 2; i++) {
        b = fa[b];
    }
    if (z & 1) {
        ans -= del(fa[b], 0) - 1;
    } else {
        ans -= del(b, 0);
    }
    cout << ans << endl;
    cal();
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

E.Chain Queries(树)

题意:

给你一棵由 n n n个顶点组成的树,这些顶点的编号从 1 1 1 n n n不等。最初,所有顶点都被染成白色或黑色。

要求您执行 q q q次查询:

  • “u”-切换顶点 u u u的颜色(如果顶点为白色,则将其变为黑色,反之亦然)。

每次查询后,您都应回答所有黑色顶点是否形成一条链。也就是说,是否存在两个黑色顶点,使得它们之间的简单路径经过所有黑色顶点,并且只经过黑色顶点。特别的,如果只有一个黑色顶点,它们会算形成一条链。如果没有黑色顶点,则它们不构成链

分析:

维护儿子黑色节点的个数为 0 , 1 , 2 , ≥ 3 0,1,2,≥3 0,1,2,3的个数即可。

考虑链的特征,以下情况均满足才合法:

  • 没有一个节点有三个黑色儿子。
  • 最多一个节点有两个黑色儿子,且该节点必须是黑色。
  • 只有一个黑色节点的父亲是白色,该黑色节点是 L C A LCA LCA

每次修改只需要更改自己的颜色,父亲对黑色儿子的记录,记录儿子黑色节点的个数的节点个数的桶,有两个黑色儿子的点集。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 1000000007;
const LL N = 2e5 + 10;
LL a[N], n, m, tot;
set<LL> q;
vector<LL> v[N];
LL cnt[N], ch[N], fa[N], col;

void dfs(LL x, LL tmp) {
    LL sum = 0;
    fa[x] = tmp;
    if (!a[fa[x]] && a[x])
        col++;
    for (auto y: v[x]) {
        if (y == fa[x])
            continue;
        if (a[y])
            sum++;
        dfs(y, x);
    }
    ch[x] = sum;
    cnt[min(sum, 3ll)]++;
    if (ch[x] == 2)
        q.insert(x);
}

void solve() {
    col = tot = 0;
    q.clear();
    cin >> n >> m;
    cnt[0] = cnt[1] = cnt[2] = cnt[3] = ch[0] = 0;
    for (int i = 1; i <= n; i++) {
        v[i].clear();
        cin >> a[i];
        tot += a[i];
        ch[i] = fa[i] = 0;
    }
    for (int i = 1; i < n; i++) {
        LL x, y;
        cin >> x >> y;
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1, 0);
    for (int i = 1; i <= m; i++) {
        LL x;
        cin >> x;
        if (a[x]) {
            cnt[min(ch[fa[x]], 3ll)]--;
            if (ch[fa[x]] == 2)
                q
                        .erase(fa[x]);
            ch[fa[x]]--;
            if (ch[fa[x]] == 2)
                q.insert(fa[x]);
            cnt[min(ch[fa[x]], 3ll)]++;
            col += ch[x] - (a[fa[x]] == 0);
            tot--;
        } else {
            cnt[min(ch[fa[x]], 3ll)]--;
            if (ch[fa[x]] == 2)
                q.erase(fa[x]);
            ch[fa[x]]++;
            if (ch[fa[x]] == 2)
                q.insert(fa[x]);
            cnt[min(ch[fa[x]], 3ll)]++;
            col -= ch[x] - (a[fa[x]] == 0);
            tot++;
        }
        a[x] ^= 1;
        if (tot < 1) {
            cout << "No" << endl;
            continue;
        }
        if (cnt[3]) {
            cout << "No" << endl;
            continue;
        }
        if (cnt[2] > 1) {
            cout << "No" << endl;
            continue;
        }
        if (col != 1) {
            cout << "No" << endl;
            continue;
        }
        if (cnt[2] == 1 && a[fa[*q.begin()]]) {
            cout << "No" << endl;
            continue;
        }
        cout << "Yes" << endl;
    }
}

int main() {
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

F.Set(分治)

题意:

将有限自然数集 T ⊆ { 0 , 1 , 2 , … } T\subseteq\{0,1,2,\ldots\} T{0,1,2,}的二进制编码定义为 f ( T ) = ∑ i ∈ T 2 i f(T)=\sum\limits_{i \in T}2^i f(T)=iT2i。例如, f ( { 0 , 2 } ) = 2 0 + 2 2 = 5 f(\{0,2\})=2^0+2^2=5 f({0,2})=20+22=5 f ( { } ) = 0 f(\{\})=0 f({})=0。请注意, f f f是从所有此类集合到所有非负整数的双射。因此, f − 1 f^{-1} f1也是有定义的。

给你一个整数 n n n 2 n − 1 2^n-1 2n1集合 V 1 , V 2 , … , V 2 n − 1 V_1,V_2,\ldots,V_{2^n-1} V1,V2,,V2n1

请找出满足以下约束条件的所有集合 S S S

  • S ⊆ { 0 , 1 , … , n − 1 } S\subseteq\{0,1,\ldots,n-1\} S{0,1,,n1}。注意 S S S可以是空的
  • 对于所有非空子集 T ⊆ { 0 , 1 , … , n − 1 } T\subseteq\{0,1,\ldots,n-1\} T{0,1,,n1}, ∣ S ∩ T ∣ ∈ V f ( T ) |S\cap T|\in V_{f(T)} STVf(T)

分析:

考虑枚举从 0 0 0 n − 1 n-1 n1的每个数字是否被 S S S包含,当枚举了前 x x x个数字后,发现只有 2 n − x 2^{n-x} 2nx个约束。

考虑枚举从 0 0 0 n − 1 n-1 n1的每个数字是否被 S S S所包含。假设当前枚举到 i i i,在剩余的约束条件中, T 1 T_1 T1 T 2 T_2 T2是两个集合,它们之间唯一的区别是是否包含 i i i T 1 T_1 T1包含 i i i):

  • S S S包含 i i i。我们可以将 T 1 T_1 T1 T 2 T_2 T2合并为新的约束条件: T ′ T' T v T ′ = ( v T 1 » 1 ) v_{T'}=(v_{T_1}»1) vT=(vT1»1)& v T 2 v_{T_2} vT2
  • S S S不包含 i i i。我们可以将 T 1 T_1 T1 T 2 T_2 T2合并为新的约束条件: T ′ T' T v T ′ = v T 1 v_{T'}=v_{T_1} vT=vT1& v T 2 v_{T_2} vT2

当枚举达到一个新数字时,就可以快速合并约束。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 24, S = (1 << 20) + 5;
int n, f[N][S] = {};
vector<int> ans;

inline void dfs(int s = 0, int i = 0) {
    if (i < n) {
        int m = 1 << (n - i - 1);
        for (int t = 0; t < m; t++)
            f[i + 1][t] = f[i][t] & f[i][m | t];
        dfs(s << 1, i + 1);
        for (int t = 0; t < m; t++)
            f[i + 1][t] = f[i][t] & (f[i][m | t] >> 1);
        dfs(s << 1 | 1, i + 1);
    } else if (f[n][0] & 1)
        ans.push_back(s);
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    f[0][0] = 1;
    for (int s = 1; s < (1 << n); s++)
        cin >> f[0][s];
    dfs();
    cout << int(ans.size()) << endl;
    for (int s: ans)
        cout << s << endl;
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值