• [Atcoder]AtCoder Beginner Contest 234 题解


    前几题直接代码。
    A - Weird Function

    int f(int x) {
        return x * x + 2 * x + 3;
    }
    cout <<  f(f(f(t)+t)+f(f(t))) << endl;
    

    B - Longest Segment

    double ans = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            ans = max(ans, 1.0 * (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
        }
    }
    printf("%.10f\n", sqrt(ans));
    

    C - Product
    输出只由0和2组成的正整数中,第k大的数。
    就是把k二进制展开就行,但是把1变成2就行。

    cin >> n;
    vector<int> ans;
    while(n) {
        ans.push_back(n % 2 * 2);
        n /= 2;
    }
    if(ans.size() == 0) cout << 0 << endl;
    else {
        for(int i = ans.size() - 1; i >= 0; i--) {
            cout << ans[i];
        }
        cout << endl;
    }
    

    D - Prefix K-th Max
    给一个排列,对于每个大于k的位置,问前面一系列数中第k大的数是多少。
    对顶堆可以nlogn,但是懒得写,直接树状数组二分也行。

    #include <bits/stdc++.h>
    
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    const int N = 5e5 + 1009;
    int n, k, a[N];
    int tree[N];
    void add(int x, int y) {
        for( ; x <= n; x += x & -x)
            tree[x] += y;
    }
    int query(int x) {
        int ans = 0;
        for( ; x; x -= x & -x)
            ans += tree[x];
        return ans;
    }
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
        freopen("data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> n >> k;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <  k; i++) add(n - a[i] + 1, 1);
        for(int i = k; i <= n; i++) {
            add(n - a[i] + 1, 1);
            int l = 1, r = n;
            while(l <= r) {
                if(query(Mid) >= k) r = Mid - 1;
                else l = Mid + 1;
            }
            cout << n - r << endl;
         }
        return 0;
    }
    

    E - Arithmetic Number
    一个数叫做等差数当且仅当满足下面两个其中之一:

    1. 它是1位或者2位数
    2. 对于每个\(i, a_i - a_{i - 1} = a_{i + 1} - a_{i}\)
      输出大于等于x的最小的等差数。

    首先,如果是只有1或2位数,直接输出就行。
    不然枚举暴力枚举第一位和第二位就可以确定后面的数,时间复杂度\(T(9 * 10 * 17)\)

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    int n, x;
    char c[209];
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
        freopen("data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> c;
        n = strlen(c);
        for(int i = 0; i < n; i++) {
            x = x * 10 + c[i] - '0';
        }
        if(n == 1 || n == 2) {
            cout << c << endl;
        } else {
            int ans = 1ll << 61;
            for(int i = 0; i < 10; i++) {
                for(int j = -9; j <= 9; j++) {
                    int t = i, now = 0;
                    int f = 1;
                    for(int k = 1; k <= n; k++) {
                        if(t > 9 || t < 0) {
                            f = 0;
                            break;
                        }
                        now = now * 10 + t;
                        t = t + j;
                    }
                    if(f && now >= x) {
                        ans = min(ans, now);
                    }
                }
    
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    F - Reordering
    给定一个字符串\(S\),问有多少个不同的字符串可以由S的一个子串排列组合构成。

    既然都是子串排列组合了,跟\(S\)就没啥关系了,只需要知道每个字母有多少个就行了。
    然后我们统计方案只要用这个数量数组来就行了。
    首先注意到组成的长度不同,方案肯定不同,可以用长度划分开方案:\(f[i][j]\)表示前\(i\)个字母组成长度为\(j\)字符串的种数。然后枚举这一个字母取多少个,然后插入新串的若干个位置,剩下的空位把原来的长度为\(j\)的串按顺序放入,就可以保证方案不同了。

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    const int mod = 998244353;
    const int N = 2e5 + 1009;
    int n, cnt[29], f[29][5009];
    char c[5009];
    int inv[N], fac[N];
    void init() {
        inv[0] = inv[1] = fac[0] = fac[1] = 1;
        for(int i = 2; i < N; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        for(int i = 2; i < N; i++) inv[i] = inv[i] * inv[i - 1] % mod;
        for(int i = 2; i < N; i++) fac[i] = fac[i - 1] * i % mod;
    }
    int C(int n, int m) {
        return fac[n] * inv[n - m] % mod * inv[m] % mod;
    }
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
        freopen("data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(0);
        cin.tie(0);
        init();
        cin >> c; n = strlen(c);
        for(int i = 0; i < n; i++) {
            cnt[c[i] - 'a' + 1]++;
        }
        int sum = 0;
        f[0][0] = 1;
        for(int i = 1; i <= 26; i++) {
            for(int j = 0; j <= sum; j++) {
                for(int k = 0; k <= cnt[i]; k++) {
                    f[i][j + k] = (f[i][j + k] + f[i - 1][j] * C(j + k, k) % mod) % mod;
                }
            }
            sum += cnt[i];
        }
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            ans = (ans + f[26][i]) % mod;
        }
        cout << ans << endl;
        return 0;
    }
    

    G - Divide a Sequence
    给定一个数组,对于一种划分成\(k\)段的方案\(P\)\(f(P)\)为每一段的最大值减最小值的乘积:\(\prod_{i = 1}^{k} max(B_i) - min(B_i)\)\(B_i\)为第\(i\)段这个集合。
    问对于所有的划分方案,\(\sum f(P)\)

    \(n^2\)的做法挺好想的,f[i]表示前i个数任意划分的乘积的和,只需要枚举最后一段的长度就行了。

    \[f(i) = \sum (f(j - 1) \times (max_{k = j}^{i} a_k - min_{k = j}^{i} a_k)) \]

    代码:

      f[0] = 1;
      for(int i = 1; i <= n; i++) {
          int maxn = a[i];
          int minn = a[i];
          for(int j = i; j >= 1; j--) {
              maxn = max(maxn, a[j]);
              minn = min(minn, a[j]);
              f[i] = (f[i] + f[j - 1] * (maxn - minn) % mod) % mod;
          }
      }
      cout << f[n] << endl;
    

    显然,跑的太慢了,考虑优化,重点就在第二个循环。
    我们给每个位置一个后缀max和min的值,每次加入i位置的值,有若干个位置的max和min会更新。
    显然,max是递减的,min是递增的。
    由于这两个问题等价,我们下面只考虑max的处理。
    对于每次加入一个新的数字,只有末尾的若干个max值会被变成\(a(i)\),同时对\(f(i)\)的影响就是\(\delta max_j * f(j)\)
    当一段数字都变成\(a(i)\)之后,其实在之后如果要改变就会一起改变相同的\(\delta max\)了,所以可以直接把所有的\(f\)值放在一起。
    这个过程就是单调栈的处理方式,我们用单调栈维护每个位置的\(sumf\)\(max\),就可以了。

    这里只计算了变大的值,不变的部分直接\(f(i) += f(i - 1)\)就行。

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    const int mod = 998244353;
    const int N = 3e5 + 1009;
    int n, a[N], f[N], submax[N], submin[N];
    struct node {
        int f, v;
    };
    stack<node> maxst, minst;
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
        freopen("data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a[i];
        maxst.push({1, a[1]});
        minst.push({1, a[1]});
        for(int i = 2; i <= n; i++) {
            int maxs = 0, mins = 0;
            f[i] = f[i - 1];
            while(!maxst.empty() && maxst.top().v < a[i]) {
                f[i] = (f[i] + maxst.top().f * (a[i] - maxst.top().v) % mod) % mod;
                maxs = (maxs + maxst.top().f) % mod;
                maxst.pop();
            }
            while(!minst.empty() && minst.top().v > a[i]) {
                f[i] = (f[i] + minst.top().f * (minst.top().v - a[i]) % mod) % mod;
                mins = (mins + minst.top().f) % mod;
                minst.pop();
            }
            maxst.push({(maxs + f[i - 1]) % mod, a[i]});
            minst.push({(mins + f[i - 1]) % mod, a[i]});
        }
        cout << f[n] << endl;
        return 0;
    }
    

    Ex - Enumerate Pairs
    很奇妙的题,看题解做出来的,这里根据自己的理解简要翻译一下官方题解。
    我们把\((x, y)\)放到\((x/k, y/k)\)(整除)这个集合中,对于每个点,跟他距离小于等于k的点只会出现在

    \[(x/k+i,y/k+j), i\in[-1, 1], j\in[-1, 1] \]

    中。
    这是显然的,假如\(x,y\)的差距超过k,那么欧几里得距离一定大于k。
    之后只要爆搜这些集合就可以找到所有的点对了,复杂度是\(O(N + M)\)

    方法很简单,但是复杂度不显然,下面是复杂度证明。
    \(B_{X,Y}\)是集合\((X,Y)\)的点数,\(f(x)\)是包含x个点的集合的点的合法点对数量。
    显然\(f(x)\le x^2\),现在要证明\(f(x)\)的下界为\(x^2\),目的是为了说明\(B_{X,Y}^2\)\(f(B_{X,Y})\)同阶。

    一个边长为\(\frac{K}{\sqrt(2)}\)的矩形中的任意两点距离小于\(K\)(最远的点对距离都为\(K\))。
    而一个边长为\(K\)的矩形可以由4个这样的矩形覆盖,假如矩形中有x个点,那么根据鸽巢原理,一定有\(\frac{x}{4}\)个点在其中一个小矩形中,那么小矩形中的点对数大于\(\frac{\frac{x}{4} * (\frac{x}{4} - 1)}{2}\)。这个数量级为\(O(n^2)\),所以\(B_{X,Y}^2\)\(f(B_{X,Y})\)同阶。

    由于\(\sum f(B_{X,Y})\le M\),得到\(O(\sum B_{X,Y}^2) = O(M)\)

    证明这个复杂度有什么用呢?

    我们的算法计算的都是

    \[\sum_{X, Y} \sum_{i = -1}^{1} \sum_{j = -1}^{1} B_{X,Y} \times B_{X + i, Y + j} \]

    根据均值不等式:

    \[B_{X,Y} \times B_{X + i, Y + j} \le \frac{1}{2} \times (B_{X,Y} ^ 2 + B_{X + i, Y + j}^2) \]

    所以

    \[\sum_{X, Y} \sum_{i = -1}^{1} \sum_{j = -1}^{1} B_{X,Y} \times B_{X + i, Y + j} \le \sum_{X, Y} \sum_{i = -1}^{1} \sum_{j = -1}^{1} \frac{1}{2} \times (B_{X,Y} ^ 2 + B_{X + i, Y + j}^2) \]

    \[\le \sum_{X, Y} \frac{9}{2} \times (B_{X,Y} ^ 2) \]

    所以:

    \[O(\sum_{X, Y} \sum_{i = -1}^{1} \sum_{j = -1}^{1} B_{X,Y} \times B_{X + i, Y + j}) = O(N + M) \]

    下面是代码:

    #include <bits/stdc++.h>
    #define int long long
    #define Mid ((l + r) / 2)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    const int N = 2e5 + 1009;
    const int lim = 2e9;
    const int dx[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
    const int dy[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
    int n, k;
    struct node {
        int x, y;
    } p[N];
    int dis(int a, int b) {
        return (p[a].x - p[b].x) * (p[a].x - p[b].x) + (p[a].y - p[b].y) * (p[a].y - p[b].y);
    }
    map<int, vector<int> > g;
    int get(int x, int y) {
        return x * lim + y;
    }
    vector<node> ans;
    signed main() {
    #ifndef ONLINE_JUDGE
        freopen("data.in", "r", stdin);
        freopen("data.out", "w", stdout);
    #endif
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> n >> k;
        for(int i = 1; i <= n; i++) {
            int x, y;
            cin >> x >> y;
            p[i] = {x, y};
            g[get(x / k, y / k)].push_back(i);
        }
        for(int i = 1; i <= n; i++) {
            int x = p[i].x / k;
            int y = p[i].y / k;
            for(int j = 0; j < 9; j++) {
                int xx = x + dx[j];
                int yy = y + dy[j];
                for(auto t : g[get(xx, yy)]) {
                    if(i >= t) continue;
                    if(dis(i, t) <= k * k) {
                        ans.push_back({i, t});
                    }
    
                }
            }
        }
        sort(ans.begin(), ans.end(), [](const node &a, const node &b) {
            if(a.x == b.x) return a.y < b.y;
            else return a.x < b.x;
        });
        cout << ans.size() << endl;
        for(auto x : ans) {
            cout << x.x << " " << x.y << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    maven工程中dubbo与spring整合
    redis在linux服务器部署
    redis在应用中使用连接不释放问题解决
    redis使用例子
    文件上传和下载(可批量上传)——基础(一)
    Hibernate各种主键生成策略与配置详解
    理解Spring、工厂模式和原始方法的说明以及对Spring的底层实现的理解
    查询文件当前目录
    Spring官网改版后下载
    Mysql事件学习
  • 原文地址:https://www.cnblogs.com/onglublog/p/15830756.html
Copyright © 2020-2023  润新知