Codeforces Round #833 (Div. 2)

A. The Ultimate Square (CF 1748 A)

题目大意

给定\(n\)块板,第\(i\)块板的宽为 \(1\),长为 \(\lceil \frac{i}{2}\rceil\)。问用这些板可以组成一个正方形,问其最大的边长是多少。

解题思路

题意可以相当于给定了长度为\(1-\frac{n}{2}\)板各两个。

\(n\)是奇数时,第一块板和第 \(n-1\)块板组合,第二块板和第\(n-2\)块板组合,刚好组成\(\frac{n-1}{2}\) 块长度为\(\lceil \frac{n}{2} \rceil\)板,加上第 \(n\)块板刚好可以组成长度为 \(\lceil \frac{n}{2} \rceil\)的正方形

\(n\)是偶数时直接前后组合,也同样可以组成长度为\(\frac{n}{2}\)的正方形。

因此答案就是 \(\lceil \frac{n}{2} \rceil\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int x;
        cin >> x;
        cout << (x + 1) / 2 << '\n';
    }

    return 0;
}


B. Diverse Substrings (CF 1748 B)

题目大意

给定一个仅包含数字的字符串,问其有多少个子串,记\(cnt_i\)表示数字 \(i\)的出现次数, \(up\)为不同数字的个数,满足所有数字 \(i\)均有 \(cnt_i \leq up\)

解题思路

注意数字种类只有\(10\)个,因此合法串的长度最长也就 \(100\),因此对长度为 \(1~100\)的所有子串暴力 判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        string s;
        cin >> s;
        LL ans = 0;
        for(int i = 0; i < s.size(); ++ i){
            map<int, int> qwq;
            set<int> ff;
            auto check = [&](){
                for(auto &i : qwq)
                    if (i.second > ff.size())
                        return false;
                return true;
            };
            for(int j = 1; j <= 100; ++ j){
                int r = i + j - 1;
                if (r >= s.size())
                    break;
                ff.insert(s[r]);
                qwq[s[r]] ++;
                if (check())
                    ++ ans;
            }
        }
        cout << ans << '\n';
    }

    return 0;
}


C. Zero-Sum Prefixes (CF 1748 C)

题目大意

给定一个长度为\(n\)的整数组 \(a\),可以将其中为\(0\)的项变为任意数字,问其前缀和为\(0\)的最大数量是多少,即最大化满足以下条件的\(k\)的数量,使得 \(\sum_{i=0}^{k} a_i = 0\)

解题思路

即前缀和\(sum_k = \sum_{i=0}^{k} a_i\)

从左到右考虑每个\(0\),假设当前考虑的下标是 \(i\),其值为\(0\), 下一个\(0\)的下标为\(j\)。考虑对前者的\(0\)更改其值,将影响\(sum_{i, ..., j-1}\)的值,即这些值中出现次数最多的是 \(val\),很显然我们将 \(0\)变成 \(-val\),这样就尽可能有更多的前缀和为 \(0\),同时也不影响 \(sum_{j...n}\)(该相同的还是相同)。

因此统计每两个\(0\)相邻之间前缀和出现最多的次数即可。注意别忘了第一个\(0\)出现前前缀和为\(0\)的数量。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n);
        for(auto &i : a)
            cin >> i;
        bool zero = false;
        map<LL, LL> cnt;
        LL ans = 0;
        LL sum = 0;
        for(int i = 0; i < n; ++ i){
            sum += a[i];
            if (a[i] == 0 && zero){
                LL id = 0, val = 0;
                for(auto &i : cnt){
                    if (val < i.second){
                        val = i.second;
                        id = i.first;
                    }
                }
                sum -= id;
                ans += val;
                cnt.clear();
            }else if (a[i] == 0){
                zero = true;
                cnt.clear();
            }else if (!zero && sum == 0)
                ++ ans;
            cnt[sum] ++;
        }
        if (zero){
            LL id = 0, val = 0;
            for(auto &i : cnt){
                if (val < i.second){
                    val = i.second;
                    id = i.first;
                }
            }
            sum -= id;
            ans += val;
        }
        cout << ans << '\n';
    }
    return 0;
}


D. ConstructOR (CF 1748 D)

题目大意

给定\(a,b,d\),要求给一个 \(x < 2^{60}\),满足

  • \(a|x\)\(d\)整除
  • \(b|x\)\(d\)整除

其中\(|\)为二进制或运算

其中\(a, b, d < 2^{30}\)

解题思路

构造题,尽量从简入手。

不妨假设\(a|x = b|x = x\)。由于\(x < 2^{60}\),而 \(a | b < 2^{30}\),我们考虑高\(30\) 位,可以列出以下方程

\[x = t \times 2^{30} + (a | b) \equiv 0 \mod d \]

如果\(d\)是偶数, \(2\)是不存在逆元的,如果此时 \((a|b)\)是奇数,那么该方程无解。但如果是偶数,我们可以所有数提一个 \(2\)出来,此时方程的成立条件并不会变化,可以继续相同判断。

假设提了 \(c\)\(2\),方程就变为

\[x = t \times 2^{30 - c} + (a | b) \equiv 0 \mod d \]

此时必有一个数是奇数,如果\((a|b)\)是奇数则无解,如果 \(d\)是奇数,则可解出\(t = inv(2)^{30-c}-(a|b)\)

继而得到 \(x\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

LL qpower(LL a, LL b, LL mo){
    LL qwq = 1;
    while(b){
        if (b & 1)
            qwq = qwq * a % mo;
        a = a * a % mo;
        b >>= 1;
    }
    return qwq;
}


LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) { x = 1; y = 0; return a; }
    LL ret = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return ret;
}

LL get_inv(LL a, LL M) {
    static LL x, y;
    assert(exgcd(a, M, x, y) == 1);
    return (x % M + M) % M;
}

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        LL a, b, d;
        cin >> a >> b >> d;
        bool ok = true;
        int sign = 0;
        while(!(d & 1)){
            if ((a & 1) || (b & 1)){
                ok = false;
                break;
            }
            d >>= 1;
            a >>= 1;
            b >>= 1;
            ++ sign;
        }
        if (!ok)
            cout << "-1" << '\n';
        else{
            LL ab = (a | b);
            LL m = d - ab % d;
            LL x = m * qpower(get_inv(2, d), 30 - sign, d) % d;
            x <<= 30;
            x += (ab << sign);
            cout << x << '\n';
        }
    }

    return 0;
}



E. Yet Another Array Counting Problem (CF 1748 E)

题目大意

给定一个整数序列\(a\),记f(l,r)表示子序列 \(a[l..r]\)中值最大且下标最小的下标。

问有多少个整数序列 \(b\),其 \(f(l,r)\)\(a\)相同。其中 \(1 \leq b_i \leq m\)

解题思路

又是最值又是下标,容易想到笛卡尔树,实际上就是问有多少个序列\(b\)的笛卡尔树和序列 \(a\)一样。

\(dp[i][j]\)表示下标为\(i\)取值为\(j\)的方案数,在笛卡尔树上直接\(dp\)即可。

\(dp[i][j] = \sum_{k=1}^{j - 1}dp[lson[i]][k] \times \sum_{l=1}^{j}dp[rson[i]][l]\)

转移的时候记录下前缀和就好了。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)

const int N = 1e6 + 8;
const LL mo = 1e9 + 7;
int n, rt, m;
LL val[N], ans;
LL G[N][2];

void build() {
    static int s[N], last;
    int p = 0;
    FOR (x, 1, n + 1) {
        last = 0;
        while (p && val[s[p - 1]] < val[x]) last = s[--p];
        if (p) G[s[p - 1]][1] = x;
        if (last) G[x][0] = last;
        s[p++] = x;
    }
    rt = s[0];
}

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        cin >> n >> m;
        for(int i = 1; i <= n; ++ i)
            cin >> val[i];
        build();
        vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
        function<void(int)> dfs = [&](int u){
            if (G[u][0] == 0 && G[u][1] == 0){
                fill(dp[u].begin(), dp[u].end(), 1);
                return;
            }
            LL lsum = 0, rsum = 0;
            LL lson = G[u][0];
            if (lson != 0)
                dfs(lson);
            LL rson = G[u][1];
            if (rson != 0)
                dfs(rson);
            for(int i = 1; i <= m; ++ i){
                rsum += dp[rson][i];
                rsum %= mo;
                dp[u][i] = (lson == 0 ? 1 : lsum) * (rson == 0 ? 1 : rsum) % mo;
                lsum += dp[lson][i];
                lsum %= mo;
            }
        };
        dfs(rt);
        ans = 0;
        for(int i = 1; i <= m; ++ i)
            ans = (ans + dp[rt][i]) % mo;
        cout << ans << '\n';
        for(int i = 0; i <= n; ++ i)
            G[i][0] = G[i][1] = 0;
    }

    return 0;
}


posted @ 2022-11-15 16:19  ~Lanly~  阅读(201)  评论(0编辑  收藏  举报