作者: KSkun

Codeforces Round #462 (Div. 2) 题解

Codeforces Round #462 (Div. 2) 题解

比赛地址:Dashboard – Codeforces Round #462 (Div. 2) – Codeforces,也可以在底下的赛题列表点进题目。

赛题列表

#A 934A – A Compatible Pair | 暴力,枚举
#B 934B – A Prosperous Lot | 贪心
#C 933A – A Twisty Movement | 枚举
#D 933B – A Determined Cleanup | 数学
#E 933C – A Colourful Prospect | 计算几何

934A A Compatible Pair

题目描述

给两个数列a, b长分别为n, m,其中a数列要删去一个数,求一种删数的方案,使得所有a_i \cdot b_j中的最大值最小。只需要输出这个最小值。
数据范围:2 \leq n, m \leq 50

样例

样例1

输入:

2 2
20 18
2 14

输出:

252

说明:
删去20,此时最大值为18*14=252。

样例2

输入:

5 3
-1 0 1 2 3
-1 0 1

输出:

2

说明:
删去3,最大值为2*1=2。

题解

枚举删去的数,然后枚举求出此时最大值,维护全部情况的最大值的最小值即可。
复杂度:O(n^3)

代码

// Code by KSkun, 2018/2
#include <cstdio>
#include <algorithm>

int n, m;
long long ans = 1e18 + 10;
long long a[55], b[55];

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    for(int i = 1; i <= m; i++) {
        scanf("%lld", &b[i]);
    }
    for(int k = 1; k <= n; k++) {
        long long mx = -1e18 - 10;
        for(int i = 1; i <= n; i++) {
            if(i == k) continue;
            for(int j = 1; j <= m; j++) {
                mx = std::max(mx, a[i] * b[j]);
            }
        }
        ans = std::min(ans, mx);
    }
    printf("%lld", ans);
    return 0;
}

934B A Prosperous Lot

题目描述

定义一个数的圈的个数为这个数中包含数码的圈的个数和,其中,数码的圈的个数依照下图计算。
180214 1 - Codeforces Round #462 (Div. 2) 题解
例如,8的圈的个数为2,而5的圈的个数为0。
给定一个数k,求任意一个不大于10^{18}的非负整数使得其圈的个数为k。如果不存在,输出-1
数据范围:1 \leq k \leq 10^6

样例

样例1

输入:

2

输出:

462

样例2

输入:

6

输出:

8080

题解

我们知道8有两个圈,而6有一个圈。先贪心一下,全用8,如果还剩下1就用1个6,加一起长度超没超18。超了就无解,没超就输出。
复杂度:O(1)

代码

// Code by KSkun, 2018/2
#include <cstdio>

int k, c2, c1;

int main() {
    scanf("%d", &k);
    c2 = k / 2;
    c1 = k % 2;
    if(c1 + c2 > 18) {
        printf("-1");
        return 0;
    } 
    if(c1) putchar('6');
    for(int i = 1; i <= c2; i++) putchar('8');
    return 0;
}

933A A Twisty Movement

题目描述

给定一个只由12构成的数列,长为n,有一次机会翻转任意区间[l, r],求一种翻转方法,使得翻转后的序列的LIS最长。只需要输出最长的LIS长度。
数据范围:1 \leq n \leq 2000

样例

样例1

输入:

4
1 2 1 2

输出:

4

说明:
翻转区间[2, 3]得到序列1 1 2 2

样例2

输入:

10
1 1 2 2 2 1 1 2 2 1

输出:

9

说明:翻转区间[3, 7]得到序列1 1 1 1 2 2 2 2 2 1

题解

这个题需要一些思考。
首先传统的LIS求法是O(n \log n)的,但是12序列可以做到O(n)的。枚举LIS中1和2的分界点,即为该点前1的个数加该点后2的个数。可以分别预处理出来。
我们考虑枚举分界点,然后枚举翻转的区间。可以发现,如果分界点不在区间内部,翻转区间是没有用的。所以区间应该包含这个分界点。翻转后的答案应该是1~区间左端点1的个数+区间左端点~分界点2的个数+分界点~区间右端点1的个数+区间右端点~n 2的个数。左端点和右端点的计算是分开的,所以可以分开枚举分别求最大值,加起来就是一个完整的最大值。
复杂度:O(n^2)

代码

// Code by KSkun, 2018/2
#include <cstdio>
#include <algorithm>

int n, a[2005], c1[2005], c2[2005], ans = 0;

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        c1[i] = c1[i - 1];
        c2[i] = c2[i - 1];
        if(a[i] == 1) {
            c1[i]++;
        } else {
            c2[i]++;
        }
    }
    for(int i = 0; i <= n; i++) {
        ans = std::max(ans, c1[i] + c2[n] - c2[i]);
    }
    for(int i = 0; i <= n; i++) {
        int mx1 = 0, mx2 = 0;
        for(int l = 1; l < i; l++) {
            mx1 = std::max(mx1, c1[l - 1] + c2[i - 1] - c2[l - 1]);
        }
        for(int r = i; r <= n; r++) {
            mx2 = std::max(mx2, c1[r] - c1[i - 1] + c2[n] - c2[r]);
        }
        ans = std::max(ans, mx1 + mx2);
    }
    printf("%d", ans);
    return 0;
}

933B A Determined Cleanup

题目描述

给两个数p和k,要求求一个多项式f(x),使得f(x)满足f(x)=q(x) \cdot (x+k)+p,其中q(x)是另一个多项式,且f(x)各项的系数和常数项都不大于k。如果无解,输出-1
数据范围:1 \leq p \leq 10^{18}, 2 \leq k \leq 2000

样例

样例1

输入:

46 2

输出:

7
0 1 0 0 1 1 1

说明:
f(x) = x^6 + x^5 + x^4 + x = (x^5 - x^4 + 3x^3 - 6x^2 + 12x - 23)(x + 2) + 46

样例2

输入:

2018 214

输出:

3
92 205 1

说明:
f(x) = x^2 + 205x + 92 = (x - 9)(x + 214) + 2018

题解

本题需要用到余式定理。余式定理的内容是多项式f(x)除以多项式(x – a)的余式为f(a)。
余式定理的证明可以参考余式定理_百度百科
这里需要f(x)除以(x + k)余p,就是说f(-k) = p。那么我们只需要将p化成一个-k进制数即可。这样看来,本题不存在无解的情况。
复杂度:O(\log p)

代码

// Code by KSkun, 2018/2
#include <cstdio>
#include <vector>

long long p;
int k;

std::vector<int> vec;

int main() {
    scanf("%lld%d", &p, &k);
    while(p != 0) {
        vec.push_back((p % k + k) % k);
        p = (vec.back() - p) / k;
    }
    printf("%d\n", vec.size());
    for(int i = 0; i < vec.size(); i++) {
        printf("%d ", vec[i]);
    } 
    return 0;
} 

933C A Colourful Prospect

题目描述

给n个圆,求n个圆将平面分成几个区域(内部不包含任何弧线)。
数据范围:1 \leq n \leq 3

样例

样例1

输入:

3
0 0 1
2 0 1
4 0 1

输出:

4

说明:
180214 2 - Codeforces Round #462 (Div. 2) 题解

样例2

输入:

3
0 0 2
3 0 2
6 0 2

输出:

6

说明:
180214 3 - Codeforces Round #462 (Div. 2) 题解

样例3

输入:

3
0 0 2
2 0 2
1 1 2

输出:

8

说明:
180214 4 - Codeforces Round #462 (Div. 2) 题解

题解

需要使用欧拉定理(V-E+F=2)解决。暂未解决。
复杂度:

[CF842D]Vitya and Strange Lesson 题解

[CF842D]Vitya and Strange Lesson 题解

题目地址:Codeforces:Problem – 842D – Codeforces、vjudge:Vitya and Strange Lesson – CodeForces 842D – Virtual Judge

题目描述

Today at the lesson Vitya learned a very interesting function — mex. Mex of a sequence of numbers is the minimum non-negative number that is not present in the sequence as element. For example, mex([4, 33, 0, 1, 1, 5]) = 2 and mex([1, 2, 3]) = 0.
Vitya quickly understood all tasks of the teacher, but can you do the same?
You are given an array consisting of n non-negative integers, and m queries. Each query is characterized by one number x and consists of the following consecutive steps:

  1. Perform the bitwise addition operation modulo 2 (xor) of each array element with the number x.
  2. Find mex of the resulting array.

Note that after each query the array changes.
给一个数列,每次查询给出一个值x,先对数列中每个元素异或上x,然后查询数列的mex值(即数列中不包含的最小非负整数)。

输入输出格式

输入格式:
First line contains two integer numbers n and m (1 ≤ n, m ≤ 3 \cdot 10^5) — number of elements in array and number of queries.
Next line contains n integer numbers ai (0 ≤ ai ≤ 3 \cdot 10^5) — elements of then array.
Each of next m lines contains query — one integer number x (0 ≤ x ≤ 3 \cdot 10^5).
输出格式:
For each query print the answer on a separate line.

输入输出样例

输入样例#1:

2 2
1 3
1
3

输出样例#1:

1
0

输入样例#2:

4 3
0 1 5 6
1
2
4

输出样例#2:

2
0
0

输入样例#3:

5 4
0 1 5 6 7
1
1
4
5

输出样例#3:

2
2
0
2

题解

冷静分析一下这题,发现异或操作是对整个数列的,这个直接在外面维护标记就行了,因为异或满足结合律。找mex是一个难点,需要用01Trie处理。
先把数列元素去个重插入01Trie中,预先算好发现数据最大是大约19位二进制,那就存20位算了,插入的同时统计一下每个子树中有多少数。每次查询的时候先把查询的这一位异或上,即如果要异或的数为1,则1当0处理,0当1处理。要异或的数为0时,选择0子树肯定是比较小的,但是要看一下这个子树有没有满,满了只能向1走,否则向0走,当然如果子树为空直接返回即可。每次走的时候维护一个结果,如果向1走要在结果的这一位上设成1。这个结果就是询问的答案。

代码

// Code by KSkun, 2018/2
#include <cstdio>
#include <algorithm>

int trie[6000005][2], tot = 1, has[6000005];

inline void insert(int n) {
    int t = 1;
    for(int i = 20; i > 0; i--) {
        has[t]++;
        if(n & (1 << (i - 1))) {
            if(!trie[t][1]) trie[t][1] = ++tot;
            t = trie[t][1];
        } else {
            if(!trie[t][0]) trie[t][0] = ++tot;
            t = trie[t][0];
        }
    }
    has[t]++;
}

int dlt = 0;

inline int query() {
    int t = 1, res = 0;
    for(int i = 20; i > 0; i--) {
        if(dlt & (1 << (i - 1))) {
            if(!trie[t][1]) {
                return res;
            } else if(has[trie[t][1]] < (1 << (i - 1))) {
                t = trie[t][1];
            } else {
                t = trie[t][0];
                res += (1 << (i - 1));
            }
        } else {
            if(!trie[t][0]) {
                return res;
            } else if(has[trie[t][0]] < (1 << (i - 1))) {
                t = trie[t][0];
            } else {
                t = trie[t][1];
                res += (1 << (i - 1));
            }
        }
    }
    return res;
}

int n, m, a[300005], t;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    std::sort(a, a + n);
    n = std::unique(a, a + n) - a;
    for(int i = 0; i < n; i++) {
        insert(a[i]);
    }
    for(int i = 0; i < m; i++) {
        scanf("%d", &t);
        dlt ^= t;
        printf("%d\n", query());
    }
    return 0;
}
Codeforces Round #461 (Div. 2) 题解

Codeforces Round #461 (Div. 2) 题解

比赛地址:Dashboard – Codeforces Round #461 (Div. 2) – Codeforces,也可以在底下的赛题列表点进题目。
由于这场比赛我并不打算也没有时间重新把代码写一遍,这里仅提供思路。

赛题列表

#A 922A – Cloning Toys | 思维
#B 922B – Magic Forest | 暴力、枚举
#C 922C – Cave Painting | 数论、暴力
#D 922D – Robot Vacuum Cleaner | 贪心
#E 922E – Birds | DP
#F 922F – Divisibility | 数论、构造

922A Cloning Toys

题目描述

最开始你有1个原始玩具,有一个玩具复制机,有两种复制模式:①输入一个原始玩具(但不消耗)并额外输出一个原始玩具和一个复制玩具;②输入一个复制玩具(但不消耗)并额外输出两个复制玩具。现在想得到x个复制玩具和y个原始玩具,想知道是否可行。
数据范围:0 \leq x, y \leq 10^9

样例

样例1

输入:

6 3

输出:

Yes

说明:
2次①,2次②。

样例2

输入:

4 2

输出:

No

样例3

输入:

1000 1001

输出:

Yes

题解

易知,y-1就是①操作的次数,这个时候只要求出x-(y-1)就能知道需要②操作进行多少次,如果这个值是奇数,显然②操作是做不到的,这个时候需要输出No,其他时候输出Yes
别忘了特殊情况!
首先,y=0绝对不可行。y=1的时候,x只能为0。以及必须满足x-(y-1)>0。
复杂度:O(1)

922B Magic Forest

题目描述

给定值n,求出满足以下条件的有序数对(a, b, c)数。

  1. 1 \leq a \leq b \leq c \leq n
  2. a \bigoplus b \bigoplus c = 0\bigoplus表示异或运算;
  3. 存在边长分别为a, b, c的三角形。

数据范围:1 \leq n \leq 2500

样例

样例1

输入:

6

输出:

1

说明:
只有一组(3, 5, 6)

样例2

输入:

10

输出:

2

题解

看到这个数据范围,就想可以枚举来做。
枚举i,j,求出i^j,然后判断答案合不合法,统计一下合法的数量。i可以从6开始枚举。
复杂度:O(n^2)

922C Cave Painting

题目描述

给定值n, k,判断是否符合条件:不存在有序实数对(i, j),满足以下条件

  1. 1 \leq i < j \leq k
  2. n \mod i = n \mod j

数据范围:1 \leq n, k \leq 10^{18}

样例

样例1

输入:

4 4

输出:

No

说明:
4 \mod 1 = 4 \mod 4 = 0

样例2

输入:

5 3

输出:

Yes

题解

让我们从1开始探索n应当满足的条件,可以发现下列式子
\begin{matrix} n \equiv 0 \ (\mod 1) \\ n \equiv 1 \ (\mod 2) \\ n \equiv 2 \ (\mod 3) \\ \cdots \\ n \equiv k - 1 \ (\mod k) \end{matrix}
这个条件一列出来,经过冷静的分析,机制的你发现了n = {\rm lcm}\{1, 2, \cdots, k\} - 1。然后再冷静分析一波,算一下k的上限,发现n取到上限的时候k应该很小(43),借用一些辅助工具甚至可以把这个表打出来。
比较可取的做法是对于比较大的k直接输出No,k比较小的时候就用上面条件验证一下。
复杂度:取决于你设的k的上限

922D Robot Vacuum Cleaner

题目描述

给你n个只包含sh的串,一个串的价值是其中的满足i\<j且i位置为s且j位置为h的(i, j)对数。求一种排序方式,使得这些串排完序拼接成的长串的价值最大。只需要输出最大价值。

数据范围:1 \leq n \leq 10^5

样例

样例1

输入:

4
ssh
hs
s
hhhs

输出:

18

说明:
按照ssshhshhhs的顺序获得最大价值。

样例2

输入:

2
h
s

输出:

1

题解

按照经验来说这种题往往都是贪心,而它确实是个贪心。依据:设s_i表示i串的s字符数,h_i表示i串的h字符数,保证\frac{s_i}{h_i}在排序中单调不增即可。
证明如下:
设想一个排序中有相邻的i, j两串,考察满足什么条件的时候交换它们的顺序答案会更优。我们发现它们内部对答案的贡献与它们的顺序无关,这个整体与排序中其他部分联合产生的贡献也与它们的顺序无关,这个时候只需要讨论它们之间产生的贡献。当s_i \cdot h_j > s_j \cdot h_i时排序不需要交换,整理就得到了s/h比较大的串应该在前面的结论。
复杂度:O(n \log n)(排序算法的复杂度)

922E Birds

题目描述

有n棵树,每棵树上有鸟巢,第i棵树的鸟巢里有ci只鸟,获得第i棵树的鸟每只花费costi能量,同时使能量上限增加B,每棵树可以自由选择获得0~ci只鸟。最开始的时候能量和能量上限都是W,从一棵树走向下一棵树的时候恢复X能量,但是拥有的能量不能超过当前能量的上限。求最后最多能获得多少鸟。
输入第一行n, W, B, X,第二行ci,第三行costi。
数据范围:1 \leq n \leq 10^3, 0 \leq W, B, X \leq 10^9, \sum_{i=1}^n c_i \leq 10^4, 0 \leq cost_i \leq 10^9

样例

样例1

输入:

2 12 0 4
3 4
4 2

输出:

6

说明:
第一棵树获得2只,第二棵获得4只。

样例2

输入:

4 1000 10 35
1 2 4 5
1000 500 250 200

输出:

5

说明:
从最后一棵树获得5只。

样例3

输入:

2 10 7 11
2 10
6 1

输出:

11

题解

发现当前剩余能量这个显然不能放进DP的维度里,于是可以考虑把这个当做DP值,把当前获得的鸟数量作为DP的一个维度,于是设计出DP的状态:dp[i][j]表示到第i课树,当前获得了j只鸟的剩余能量。
然后就可以推一下方程了
dp[i][j] = \max \{\min\{dp[i-1][j-k]+X, W+(j-k)*B\} - k*cost_i\}
然后我们发现它应该是一个O(n)的转移,枚举k即可。
复杂度:O(n^2)

922F Divisibility

题目描述

给定n,找到一个\{1, 2, \cdots, n\}的子集I,满足I中满足a, b都来自I,a\<b且a|b的有序实数对(a, b)的数量为k。
如果找不到,输出No,如果找得到,输出一种I。
数据范围:2 \leq n \leq 3 \times 10^5, 1 \leq k \leq \min (10^9, \frac{n(n-1)}{2})

样例

样例1

输入:

3 3

输出:

No

样例2

输入:

6 6

输出:

Yes
5
1 2 4 5 6

说明:
有序实数对是(1, 2)(1, 4)(1, 5)(1, 6)(2, 4)(2, 6)。

样例3

输入:

8 3

输出:

Yes
4
2 4 5 8

说明:
有序实数对是(2, 4)(2, 8)(4, 8)。

题解

首先说判定。当k比集合\{1, 2, \cdots, n\}产生的答案还大的时候,就不存在。这个集合产生的答案就是每个数字除了它本身和1以外的因子数,这个可以O(n \log n)预处理出来再前缀和得到。
接下来构造。首先找到一个前缀和不大于k的最大位置pos,我们要选出答案为k的集合,换句话就是说要在\{1, 2, \cdots, pos\}删元素删到答案为k,考虑从pos/2~pos这一段开始删,这一段的因子数本来就比较大,而且删除这些数并不会对比其他数对答案的贡献产生影响。删完以后把剩下的输出就行。
复杂度:O(n \log n+2n)