Codeforces Round #531 (Div. 3)

Contests 链接:Codeforces Round #531 (Div. 3)

A. Integer Sequence Dividing

题意

给定一个整数 n n n,将 [ 1 , n ] [1,n] [1,n] 以内所有的整数分到两个集合 A A A B B B 中,每个整数只能被分到一个集合,要求两个集合中所有数字的和的差的绝对值最小,即: ∣ s u m A − s u m B ∣ |sum_A-sum_B| sumAsumB 的值最小,其中空集合内所有数字的和为 0 0 0

输入

输入仅包含一个整数 n   ( 1 ≤ n ≤ 2 × 1 0 9 ) n~(1\leq n\leq2\times10^9) n (1n2×109)

输出

输出题目所求答案。

样例

输入
3
输出
0
提示
其中一种合法的分法是:令 A = { 1 , 2 } , B = { 3 } A=\{1,2\},B=\{3\} A={1,2},B={3},这样 ∥ s u m A − s u m B ∥ = 0 \|sum_A-sum_B\|=0 sumAsumB=0
输入
5
输出
1
提示
其中一种合法的分法是:令 A = { 1 , 3 , 4 } , B = { 2 , 5 } A=\{1,3,4\},B=\{2,5\} A={1,3,4},B={2,5},这样 ∥ s u m A − s u m B ∥ = 1 \|sum_A-sum_B\|=1 sumAsumB=1
输入
6
输出
1
提示
其中一种合法的分法是:令 A = { 1 , 4 , 5 } , B = { 2 , 3 , 6 } A=\{1,4,5\},B=\{2,3,6\} A={1,4,5},B={2,3,6},这样 ∥ s u m A − s u m B ∥ = 1 \|sum_A-sum_B\|=1 sumAsumB=1

题解

通过多次枚举可以发现,如果 n n n 4 4 4 的整数倍,那么对于每 4 4 4 个数字,就可以用 a 1 + a 4 = a 2 + a 3 a_1+a_4=a_2+a_3 a1+a4=a2+a3 将第 1 1 1 个和第 4 4 4 个分到一个集合中,剩下的分到另一个集合中,而对于 n n n 不是 4 4 4 的整数倍的情况,可以将多出来的 x   ( 0 < x < 4 ) x~(0<x<4) x (0<x<4) 个数按照 n = 1 , 2 , 3 n=1,2,3 n=1,2,3 时进行划分,就可以得到最小答案。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
int n;
int ans[10] = {0, 1, 1, 0};

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d", &n) != EOF) {
        printf("%d\n", ans[n % 4]);
    }

    return 0;
}

B. Array K-Coloring

题意

给定 n n n 个整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an k k k 种颜色,要求用这 k k k 种颜色给所有整数上色,并且满足下面三个要求:

  1. 每个整数都必须要涂上一种颜色;
  2. 每种颜色至少要被涂上一次;
  3. 所有被颜色 i i i 涂上的数字都必须是不同的。

若存在满足上述条件的涂色方案,则输出任意一种,否则输出 N O NO NO

输入

第一行为两个整数 n , k   ( 1 ≤ k ≤ n ≤ 5000 ) n,k~(1\leq k\leq n\leq5000) n,k (1kn5000),第二行为 n n n 个整数 a 1 , a 2 , ⋯   , a n   ( 1 ≤ a i ≤ 5000 ) a_1,a_2,\cdots,a_n~(1\leq a_i\leq5000) a1,a2,,an (1ai5000)

输出

如果不存在合法的解,则输出 N O NO NO,否则在第一行输出 Y E S YES YES,并在第二行给出任意一种合法的解,每种颜色用数字 [ 1 , k ] [1,k] [1,k] 内的整数表示。

样例

输入
4 2
1 2 2 3
输出
YES
1 1 2 2
提示
2   1   2   1 2~1~2~1 2 1 2 1 也是一种合法的涂色方案,当然也存在其他合法的涂色方案。
输入
5 2
3 2 1 2 3
输出
YES
2 1 1 2 1
提示
1   1   1   2   2 1~1~1~2~2 1 1 1 2 2 也是一种合法的涂色方案,当然也存在其他合法的涂色方案。
输入
5 2
2 1 1 2 1
输出
NO

题解

若存在任意一个颜色出现的次数大于 k k k,则输出 N O NO NO,否则将每个数字 x x x 出现的位置存在一个 vector 数组的第 x x x 个位置上,然后按下标从小到大遍历这个 vector 数组,按 [ 1 , k ] [1,k] [1,k] 的循环给所有位置上色。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
const int maxn = 5000 + 100;
int x, n, k;
int ans[maxn];
vector<int> G[maxn];

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d%d", &n, &k) != EOF) {
        for(int i = 0; i < maxn; ++i) {
            G[i].clear();
        }
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &x);
            G[x].push_back(i);
        }
        int Index = 0;
        bool flag = true;
        for(int i = 0; i < maxn; ++i) {
            int len = G[i].size();
            if(len > k) {
                flag = false;
                break;
            }
            for(int j = 0; j < len; ++j) {
                int pos = G[i][j];
                ans[pos] = Index;
                Index = (Index + 1) % k;
            }
        }
        if(!flag) {
            printf("NO\n");
            continue;
        }
        printf("YES\n");
        for(int i = 1; i <= n; ++i) {
            if(i != 1) {
                printf(" ");
            }
            printf("%d", ans[i] + 1);
        }
        printf("\n");
    }

    return 0;
}

C. Doors Breaking and Repairing

题意

有两个人玩一个游戏,最开始有 n n n 扇门,第 i i i 扇门初始的耐久性为 a i a_i ai,你是先手,另一个人后手,每当你选择一扇门,可以将这扇门的当前耐久度 b i b_i bi 改为 max ⁡ ( 0 , b i + 0 ) \max(0,b_i+0) max(0,bi+0),每当另一个人选择一扇门,若这扇门的当前耐久度不为 0 0 0,则可以将其修改为 b i + y b_i+y bi+y,你希望有最多的门的耐久度变为 0 0 0,而另一个人希望有最少的门的耐久度变为 0 0 0,两人都采取最优策略,问经过 1 0 100 10^{100} 10100 轮游戏之后,最多有多少扇门的耐久度变为 0 0 0

输入

第一行为 3 3 3 个整数 n , x , y   ( 1 ≤ n ≤ 100 , 1 ≤ x , y ≤ 1 0 5 ) n,x,y~(1\leq n\leq100,1\leq x,y\leq10^5) n,x,y (1n100,1x,y105),第二行有 n n n 个整数 a 1 , a 2 , ⋯   , a n   ( 1 ≤ a i ≤ 1 0 5 ) a_1,a_2,\cdots,a_n~(1\leq a_i\leq10^5) a1,a2,,an (1ai105)

输出

输出题目所求答案。

样例

输入
6 3 2
2 3 1 3 4 2
输出
6
输入
5 3 3
1 2 4 2 3
输出
2
输入
5 5 6
1 2 6 10 3
输出
2

题解

x > y x>y x>y,则先手一定能将所有门的耐久度都变为 0 0 0,否则若先手破坏一道 a i > x a_i>x ai>x 的门,后手就可以立即对第 i i i 扇门进行修复,所以先手只能去破坏 a i ≤ x a_i\leq x aix 的门,而后手一定会去修复还没有被破坏的 a i ≤ x a_i\leq x aix 的门,所以先手最多只能破坏 ⌈ c n t 2 ⌉ \lceil\frac{cnt}{2}\rceil 2cnt 扇门,其中 c n t cnt cnt a i ≤ x a_i\leq x aix 的门的数量。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
int n, x, y, num, cnt;

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d%d%d", &n, &x, &y) != EOF) {
        cnt = 0;
        for(int i = 0; i < n; ++i) {
            scanf("%d", &num);
            if(num <= x) {
                ++cnt;
            }
        }
        if(x > y) {
            printf("%d\n", n);
            continue;
        } else {
            printf("%d\n", (cnt + 1) / 2);
        }
    }

    return 0;
}

D. Balanced Ternary String

题意

给定一个长度为 n n n 的三元字符串,字符串中只包含三种字符:0 1 2,每次可以将字符串中的一个字符替换成另一个字符,要求替换最少的次数,使得这个三元字符串每种字符的个数相等,如果有多种答案,输出字典序最小的一种。

输入

第一行包含一个整数 n   ( 3 ≤ n ≤ 3 × 1 0 5 , 3 ∣ n ) n~(3\leq n\leq3\times10^5,3|n) n (3n3×105,3n),第二行为一个长度为 n n n 的三元字符串。

输出

输出题目所求答案。

样例

输入
3
121
输出
021
输入
6
000000
输出
001122
输入
6
211200
输出
211200
输入
6
120110
输出
120120

题解

先统计字符 0 1 2 出现的次数 c n t 0 , 1 , 2 cnt_{0,1,2} cnt0,1,2,然后先从前往后确定所有 0 0 0 的位置,若 c n t 0 ≥ n 3 cnt_0\geq\frac{n}{3} cnt03n,则将前 n 3 \frac{n}{3} 3n 0 0 0 标为 t r u e true true,表示完全确定,并且不能更改,多出来的 0 f a l s e false false,表示可以更改,若 c n t 0 < n 3 cnt_0<\frac{n}{3} cnt0<3n,则将靠前的非 0 c n t cnt cnt 值大于 n 3 \frac{n}{3} 3n 的字符改为 0,并修改相应的 c n t cnt cnt 值,并将该位置标为 t r u e true true,对于 12 也是如此。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
const int maxn = 300000 + 100;
int n;
int cnt[3];
bool vis[maxn];
char str[maxn];

int id(char ch) {
    return ch - '0';
}

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d%s", &n, str) != EOF) {
        int len = strlen(str);
        memset(cnt, 0, sizeof(cnt));
        for(int i = 0; i < len; ++i) {
            ++cnt[id(str[i])];
            vis[i] = false;
        }
        for(int i = 0; i < 3; ++i) {
            if(cnt[i] > n / 3) {
                int tmp = n / 3;
                for(int j = 0; j < len; ++j) {
                    if(str[j] == '0' + i) {
                        vis[j] = true;
                        --tmp;
                        if(tmp == 0) {
                            break;
                        }
                    }
                }
            } else {
                for(int j = 0; j < len; ++j) {
                    if(str[j] == '0' + i) {
                        vis[j] = true;
                    } else {
                        if(!vis[j] && cnt[id(str[j])] > n / 3 && cnt[i] < n / 3) {
                            --cnt[id(str[j])];
                            str[j] = '0' + i;
                            ++cnt[i];
                            vis[j] = true;
                        }
                    }
                }
            }
        }
        printf("%s\n", str);
    }

    return 0;
}

E. Monotonic Renumeration

题意

给定一个长度为 n n n 的序列 a a a,要求生成一个满足以下条件的序列 b b b

  1. b 1 = 0 b_1=0 b1=0
  2. 对于任意的 1 ≤ i , j ≤ n 1\leq i,j\leq n 1i,jn,若 a i = a j a_i=a_j ai=aj,则 b i = b j b_i=b_j bi=bj(若 a i ≠ a j a_i\neq a_j ai̸=aj,也可以使得 b i = b j b_i=b_j bi=bj);
  3. 对于每一个 i ∈ [ 1 , n ) i\in[1,n) i[1,n),有 b i = b i + 1 b_i=b_{i+1} bi=bi+1 或者 b i + 1 = b i + 1 b_i+1=b_{i+1} bi+1=bi+1

统计总共有多少种 b b b 序列满足条件,将答案对 998244353 998244353 998244353 取模后输出。

输入

第一行包含一个整数 n   ( 2 ≤ n ≤ 2 × 1 0 5 ) n~(2\leq n\leq2\times10^5) n (2n2×105),第二行包含 n n n 个整数 a 1 , a 2 , ⋯   , a n   ( 1 ≤ a i ≤ 1 0 9 ) a_1,a_2,\cdots,a_n~(1\leq a_i\leq10^9) a1,a2,,an (1ai109)

输出

输出题目所求答案。

样例

输入
5
1 2 1 2 3
输出
2
输入
2
100 1
输出
2
输入
4
1 3 3 7
输出
4

题解

2 2 2 3 3 3 可知若存在 a l = a r   ( l ≤ r ) a_l=a_r~(l\leq r) al=ar (lr),则对于任意 i , j ∈ [ l , r ] i,j\in[l,r] i,j[l,r],有 b i = b j b_i=b_j bi=bj,因此所有 n n n 个整数被分为 x x x 个区间,每个区间内的 b i b_i bi 值都必须相等,答案就是 2 x − 1 m o d    998244353 2^{x-1}\mod998244353 2x1mod998244353
先记录每个数字 a i a_i ai 最后出现的下标 E n d a i End_{a_i} Endai,然后将 j j j i i i E n d a i End_{a_i} Endai 扫一遍,在扫的过程中将所有 E n d a j > E n d a i End_{a_j}>End_{a_i} Endaj>Endai 的值更新到 E n d a i End_{a_i} Endai 中,直到停止扫描,则刚刚扫过的整个区间的 b j b_j bj 的值都必须相同。即上述的 x x x 个区间中的其中一个。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
const int maxn = 200000 + 100;
const LL MOD = 998244353;
int n;
int num[maxn], End[maxn];
vector<int> sand;

LL fast_pow(LL res, LL n) {
    LL ans;
    for(ans = 1; n != 0; n >>= 1) {
        if((n & 1) == 1) {
            ans = ans * res % MOD;
        }
        res = res * res % MOD;
    }
    return ans;
}

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d", &n) != EOF) {
        sand.clear();
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &num[i]);
            sand.push_back(num[i]);
        }
        sort(sand.begin(), sand.end());
        sand.erase(unique(sand.begin(), sand.end()), sand.end());
        for(int i = 1; i <= n; ++i) {
            num[i] = lower_bound(sand.begin(), sand.end(), num[i]) - sand.begin() + 1;
            End[num[i]] = i;
        }
        LL ans = 0;
        int Begin = 1;
        int EEnd;
        while(Begin != n + 1) {
            EEnd = End[num[Begin]];
            ++ans;
            for(int i = Begin; i <= EEnd; ++i) {
                EEnd = max(EEnd, End[num[i]]);
            }
            Begin = EEnd + 1;
        }
        printf("%I64d\n", fast_pow(2, ans - 1));
    }

    return 0;
}

F. Elongated Matrix

题意

有一个 n × m n\times m n×m 的矩阵,可以对这个矩阵进行交换任意两行的操作(也可以不进行任何操作),但是列的顺序不能被交换,要求交换完毕后,将最后的矩阵进行按列扫描(即按 a 1 , 1 , a 2 , 1 , ⋯   , a n , 1 , a 1 , 2 , a 2 , 2 , ⋯   , a n , 2 , ⋯   , a n , m a_{1,1},a_{2,1},\cdots,a_{n,1},a_{1,2},a_{2,2},\cdots,a_{n,2},\cdots,a_{n,m} a1,1,a2,1,,an,1,a1,2,a2,2,,an,2,,an,m 的顺序扫描),得到一个序列 s 1 , s 2 , ⋯   , s n m s_1,s_2,\cdots,s_{nm} s1,s2,,snm,序列 s s s 的任意相邻两项之间满足 ∣ s i − s i + 1 ∣ ≥ k   ( 1 ≤ i < n m ) |s_i-s_{i+1}|\geq k~(1\leq i<nm) sisi+1k (1i<nm),求最大的 k k k 值。

输入

第一行为两个整数 n , m   ( 1 ≤ n ≤ 16 , 1 ≤ m ≤ 1 0 4 , 2 ≤ n m ) n,m~(1\leq n\leq16,1\leq m\leq10^4,2\leq nm) n,m (1n16,1m104,2nm),接下去 n n n 行每行 m m m 个整数,第 i i i 行第 j j j 列的整数 a i , j a_{i,j} ai,j 表示矩阵对应位置的整数,其中 1 ≤ a i , j ≤ 1 0 9 1\leq a_{i,j}\leq10^9 1ai,j109

输出

输出题目所求答案。

样例

输入
4 2
9 9
10 8
5 3
4 3
输出
5
提示
将矩阵按行交换成如下矩阵,即可得到最大的 k k k 5 5 5
5 3
10 8
4 3
9 9
此时 s s s 序列为: [ 5 , 10 , 4 , 9 , 3 , 8 , 3 , 9 ] [5,10,4,9,3,8,3,9] [5,10,4,9,3,8,3,9],任意两个相邻元素之间的差值至少为 5 5 5
输入
2 4
1 2 3 4
10 3 7 3
输出
0
提示
将行按任意顺序排列,得到的 k k k 值都为 0 0 0
输入
6 1
3
6
2
5
1
4
输出
3
提示
原序列的 k k k 值已经为 3 3 3

题解

由于对于任意两行 i i i j j j,只要 i i i j j j 的下一行,那么这两行中任意一列的差值都是定值,且从第 i i i 行到第 j j j 行的距离必然为 d i s [ i ] [ j ] = min ⁡ ( ∣ a i , l − a j , l ∣ ) , l ∈ [ 1 , m ] dis[i][j]=\min(|a_{i,l}-a_{j,l}|),l\in[1,m] dis[i][j]=min(ai,laj,l),l[1,m],因此可以将 m m m 列处理成 1 1 1 列,不考虑跨列连接的问题的话,就变成了 n n n 1 1 1 列的问题了。
定义 d p [ s ] [ t ] [ l ] dp[s][t][l] dp[s][t][l] 表示以第 s s s 行为起点,第 t t t 列为终点, l l l 的二进制位中所有为 1 1 1 的位都被访问过的状态的最大满足条件的距离,假设从 s s s 到中转行 c c c 的遍历行数的状态为 l l l,那么到达一个新的行 t t t 的距离,可以用状态转移方程得到:
d p [ s ] [ t ] [ l ∣ ( 1 < < t ) ] = max ⁡ ( min ⁡ ( d p [ s ] [ c ] [ l ] , d i s [ c ] [ t ] ) ) ( b i t l , s = b i t l , c = 1 , b i t l , t = 0 ) dp[s][t][l|(1<<t)]=\max(\min(dp[s][c][l],dis[c][t]))\quad(bit_{l,s}=bit_{l,c}=1,bit_{l,t}=0) dp[s][t][l(1<<t)]=max(min(dp[s][c][l],dis[c][t]))(bitl,s=bitl,c=1,bitl,t=0)

其中 b i t a , b bit_{a,b} bita,b 表示 a a a 的第 b b b 个二进制位的值。 d p dp dp 的初始值为 d p [ i ] [ j ] [ k ] = { ∞ i = j , k = 1 < < i 0 o t h e r s dp[i][j][k]= \begin{cases} \infty&i=j,k=1<<i\\ 0&others \end{cases} dp[i][j][k]={0i=j,k=1<<iothers

最后考虑跨列的情况,即以 s s s 为起始行, t t t 为最终行,从 t t t 行的当前列到 s s s 行的下一列,其距离为: d i s _ n e x t [ t ] [ s ] = min ⁡ ( ∣ a t , j − a s , j + 1 ∣ )   ( s ≠ t , 1 ≤ j < m ) dis\_next[t][s]=\min(|a_{t,j}-a_{s,j+1}|)~(s\neq t,1\leq j<m) dis_next[t][s]=min(at,jas,j+1) (s̸=t,1j<m),将所有 d p [ s ] [ t ] [ l ] dp[s][t][l] dp[s][t][l] 与对应的 d i s _ n e x t dis\_next dis_next min ⁡ \min min,再取所有的最大值,就是答案。
注意特判 n = 1 n=1 n=1 的情况。

过题代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define LL long long
const int maxn = 16;
const int maxm = 10000 + 100;
int n, m;
int num[maxn][maxm];
int dp[maxn][maxn][1 << maxn];
int dis[maxn][maxn], dis_next[maxn][maxn];

int main() {
    #ifdef Dmaxiya
    freopen("test.txt", "r", stdin);
    #endif // Dmaxiya
    ios::sync_with_stdio(false);

    while(scanf("%d%d", &n, &m) != EOF) {
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < m; ++j) {
                scanf("%d", &num[i][j]);
            }
        }
        if(n == 1) {
            int ans = INT_MAX;
            for(int i = 1; i < m; ++i) {
                ans = min(ans, abs(num[0][i] - num[0][i - 1]));
            }
            printf("%d\n", ans);
            continue;
        }
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < n; ++j) {
                dis[i][j] = INT_MAX;
                dis_next[i][j] = INT_MAX;
                if(i == j) {
                    continue;
                }
                for(int k = 0; k < m; ++k) {
                    dis[i][j] = min(dis[i][j], abs(num[i][k] - num[j][k]));
                    if(k != 0) {
                        dis_next[i][j] = min(dis_next[i][j], abs(num[i][k - 1] - num[j][k]));
                    }
                }
            }
        }
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < n; ++i) {
            dp[i][i][1 << i] = INT_MAX;
        }
        for(int i = 1; i < (1 << n); ++i) {
            for(int s = 0; s < n; ++s) {
                if(((i >> s) & 1) == 0) {
                    continue;
                }
                for(int t = 0; t < n; ++t) {
                    if(s == t || ((i >> t) & 1) == 1) {
                        continue;
                    }
                    for(int j = 0; j < n; ++j) {
                        if(((i >> j) & 1) == 0) {
                            continue;
                        }
                        int tmp = i | (1 << t);
                        dp[s][t][tmp] = max(dp[s][t][tmp], min(dp[s][j][i], dis[j][t]));
                    }
                }
            }
        }
        int ans = 0;
        int tmp = (1 << n) - 1;
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < n; ++j) {
                if(i == j) {
                    continue;
                }
                ans = max(ans, min(dp[i][j][tmp], dis_next[j][i]));
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}

你可能感兴趣的:(Codeforces)