Featured image of post Codeforces Round #1075(Div.2)

Codeforces Round #1075(Div.2)

掉大分,但是这场也挺edu的。

C1

题目大意:给定 $n$ ,求一个长度为 $n$ 的排列 $p$ ,使得对于每个 $i(2 \leq i \leq n-1)$ 都存在一个 $j(i \leq j \leq n),p_i=p_j \bigoplus i$ 。

数据范围: $3 \leq n \leq 2 \cdot 10^5$

思路:第一反应,猜测这个影响所有数的的数是 $1$ 或者 $n$ ,然后打表发现是行的,直接冲。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    a[n - 1] = 1;
    map<int, int> ma;
    for (int i = 2; i <= n - 1; i++) ma[1 ^ i]++;
    int idx = 1;
    for (int i = 2; i <= n; i++) {
        if (!ma.count(i)) {
            idx = i;
            break;
        }
    }
    a[0] = idx;
    for (int i = 1; i < n - 1; i++) {
        a[i] = 1 ^ (i + 1);
    }
    for (int p : a) cout << p << ' ';
    cout << endl;
    return;
}

C2

题目大意:跟上题一样,但是 $1 \leq i \leq n-1$ ,如果不行就输出-1。

数据范围: $3 \leq n \leq 2 \cdot 10^5$

思路:赛时没有开出来,肯定跟上题有关系,但不知道是什么关系。

首先,打表发现 $n=2^x$ 的时候是不行的,为什么?

若 $n=2^x$ 位于 $0 \sim n-2$ 的位置,则必然存在它后面有一个数能成立,而由原来的公式是不可行的。

那么,当 $n$ 在 $n-1$ 的位置呢?此时考虑 $n-2$ 位置的数,必然是 $n \bigoplus (n-1) = p_{n-2}$ ,这显然也不对。

然后,再看可行的构造,由 $C1$ 题打表后发现可以交换 $a_1$ 跟 $a_{lowbit(n)}$ 位置的数,为什么?(一个启示:位运算题lowbit是很重要的)

由于有 $p_1 =n,p_{lowbit(n)}=p_k \bigoplus lowbit(n),$ 而 $p_{lowbit(n)}=lowbit(n) \bigoplus 1=lowbit(n)+1$ 因此往前交换后会发现, $p_1=lowbit(n)+1= lowbit(n) \bigoplus 1,p_{lowbit(n)}=n = (n \bigoplus lowbit(n)) \bigoplus lowbit(n)$ ,前者自然没啥问题,后者由于 $n-lowbit(n)=(n-lowbit(n)+1) \bigoplus 1$ ,因此也在它后面。

这时候会问,如果 $n$ 是奇数呢?奇数情况完全不需要交换,用C1构造的数列就能成立。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void solve() {
    int n;
    cin >> n;
    int tem = popcount((unsigned)n);
    if (tem == 1) {
        cout << -1 << endl;
        return;
    }
    vector<int> a(n + 1);
    map<int, int> ma;
    a[n] = 1;
    ma[1]++;
    for (int i = 2; i <= n - 1; i++) {
        a[i] = (i ^ 1);
        ma[i ^ 1]++;
    }
    for (int i = 1; i <= n; i++) {
        if (!ma.count(i)) {
            a[1] = i;
            break;
        }
    }
    swap(a[1], a[lowbit(n)]);
    for (int i = 1; i <= n; i++) cout << a[i] << ' ';
    cout << endl;
    return;
}

D1

题目大意:给定一个字符串 $s$ ,在D1中它只是0-1字符串, $s[i]=0$ 代表在数组 $0 \sim n-1$ 的排列中不会出现子数组的MEX值为 $i$ ,反之则出现子数组的MEX值为 $i$ ,又给定一个数 $c$ ,求这样子数组不被 $c$ 整除的最小值,并模 $10^9+7$ ,若不存在则输出-1。

数据范围: $3 \leq n \leq 2 \cdot 10^5,1 \leq c \leq 10^9$

思路:组合数学题,而且这题的 $c$ 用处不大,因此原题中能构造的排列数是定值。

首先,如果字符串的首位和末位是0,那么肯定是不行的,因此输出-1。

随后考虑插空法,这是一个很常见的方法,也就是从MEX的角度考虑逐步拓展,从 $2 \sim n-1$ ,此时存在连续子数组能够表示 $0 \sim i-1$ 了,如果不能表示MEX为 $i$ 的话,就把 $i$ 插进这个子数组中,反之插到它的两侧。

于是,这样计数即可,需要注意的是 $c$ 可以每次直接除一下公因数,判断最后是否为1。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const int MOD = 1e9 + 7;
void solve() {
    int n, c;
    cin >> n >> c;
    string s;
    cin >> s;
    if (s[0] == '0' || s[n - 1] == '0') {
        cout << -1 << endl;
        return;
    }
    ll res = 1;
    map<int, int> ma;
    for (int i = 1; i <= n - 1; i++) {
        if (s[i] == '1') {
            res = res * 2;
            res %= MOD;
            c /= __gcd(c, 2);
        } else {
            res = res * i;
            res %= MOD;
            c /= __gcd(c, i);
        }
    }
    cout << (c == 1 ? -1 : res) << endl;
    return;
}

D2

题目大意:跟上题一样,但是字符串中存在?,表示不知道取0还是取1。

数据范围:跟上题一样。

思路:其实很简单,首先先把所有不是?的对应元素乘出来(最后一格跟第一格由于有默认元素了,也能算),然后考虑对应?的元素,考虑贪心。

首先,如果用1是偶数的话,那么肯定不如取2,再考虑奇数,此时从高位倒着遍历到低位,如果此时剩下的 $c$ 没有奇数因子,那么肯定此时能给的2都给高位,如果有奇数因子,那么可以全部取 $2$ 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
constexpr int MOD = 1e9 + 7;
void solve() {
    int n, c;
    cin >> n >> c;
    string s;
    cin >> s;
    if (s[0] == '0' || s[n - 1] == '0') {
        cout << -1 << endl;
        return;
    }
    ll ans = 2;
    c /= __gcd(c, 2);
    vi res;
    rep(i, 1, n - 2) {
        if (s[i] == '0') {
            ans = ans * i % MOD;
            c /= __gcd(c, i);
        } else if (s[i] == '1') {
            ans = ans * 2 % MOD;
            c /= __gcd(c, 2);
        } else
            res.push_back(i);
    }
    frep(i, sz(res) - 1, 0) {
        if (res[i] % 2 == 0) {
            ans = ans * 2 % MOD;
            c /= __gcd(c, 2);
        }
    }
    frep(i, sz(res) - 1, 0) {
        if (res[i] == 1 || res[i] % 2 == 0) continue;
        if (c <= 2) {
            ans = ans * res[i] % MOD;
            c /= __gcd(c, res[i]);
        } else {
            ans = ans * 2 % MOD;
            c /= __gcd(c, 2);
        }
    }
    cout << ((c == 1) ? -1 : ans) << endl;
    return;
}

这里给出一道类题:CF1699C