AtCoder Beginner Contest 353

前面两道阅读理解直接跳过。

C - Sigma Problem

大意

有一函数f(x,y)=(x+y) \mod10^8

给定长度为n的序列a,求\sum_{i=1}^{n-1}\sum_{j=i+1}^n f(a_i,a_j)

思路

我们将f变一下:

f(x, y) = \begin{cases} x + y, & \text{if $x + y < 10^8$ } \\ x + y - 10^8, & \text{otherwise} \end{cases}

那么可以先算出\sum_{i=1}^{n-1}\sum_{j=i+1}^n a_i + a_j,再减去满足以下条件的数对(i,j)个数\times 10^8

  • 1\le i \le j \le n
  • a_i+a_j\ge 10^8

考虑求前式,由于每项都相加n-1次,所以可以改写为(n-1)\sum_{i=1}^{n}a_i,用O(n)求。

接下来,求a_i+a_j \ge 10^8的整数对(i,j)的个数。

a从小到大排序,我们可以发现,随着i的增大,满足a_i+a_j \ge 10^8j最小值是单调递减的,可以用双指针(放缩法)求得。注意在a_i=5\times 10^7的情况下不能重复计算。

代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int mod = 1e8;
#define int long long

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n;
    cin >> n;

    vector<int> a(n);
    for (auto &i: a) cin >> i;
    sort(a.begin(), a.end());

    int ans = 0, cnt = 0, r = n;
    for (int i = 0; i < n; i++){
        ans += a[i] * (n - 1);
        r = max(r, i + 1);
        while(r - 1 > i && a[r - 1] + a[i] >= mod) r--;
        cnt += n - r;
    }
    ans -= cnt * mod;
    cout << ans << endl;
    
    return 0;
}

D - Another Sigma Problem 

大意

定义f(x,y)表示把y直接拼接到x后面所形成的整数。

给定长度为n的序列a,求\sum_{i=1}^{n-1}\sum_{j=i+1}^n f(a_i,a_j)

思路

\operatorname{len}(x)x位数,则可以式子变形为f(x,y)=x\times 10^{\operatorname{len}(y)} + y.

将每个数a_i的贡献拆成两个部分计算:f(*,a_i)f(a_i,*)

前者很容易,因为a_i被原封不动地加了n-1次,因此对答案的贡献为(i-1)a_i

接着考虑后者,可以使用后缀和,设d_x表示a_i \to a_nx位数的个数,那么对答案的贡献为\sum_{k=1}^{10}d_k\times 10^k \times a_i

代码

#include <iostream>
#include <vector>
#include <string>
#include "atcoder/modint"
using namespace std;

typedef atcoder::modint998244353 Z;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n;
    cin >> n;

    vector<int> a(n), d(11);
    
    auto len = [&](int x){
        return to_string(x).size();
    };

    for(auto &i: a){
        cin >> i;
        d[len(i)]++;
    }

    Z ans = 0;
    vector<Z> xp(11, 1);
    for(int i = 1; i <= 10; i++) xp[i] = xp[i - 1] * 10;

    for(int i = 0; i < n; i++){
        ans += Z(a[i]) * i;
        d[len(a[i])]--;
        for(int j = 1; j <= 10; j++) ans += xp[j] * a[i] * d[j];
    }
    cout << ans.val() << endl;
    return 0;
}

E - Yet Another Sigma Problem

大意

定义f(x,y)表示字符串x和字符串y最长公共前缀的长度。

给定n个字符串s=(s_1,s_2,\cdots,s_n),求\sum_{i=1}^{n-1}\sum_{j=i+1}^n f(s_i,s_j)

思路

看到LCPTrie树,因为它会把所有具有相同前缀的字符串合并。

我们依次将每个字符串插入Trie树,每个节点都保留了以它所代表的字符串为前缀的字符串的数量信息。

因此在到达每个节点后,答案都加上cnt_p

最后输出答案即可。

代码

#include <iostream>
#include <array>
#include <vector>
using namespace std;

#define int long long

struct trie {
    using M = array<int, 26>;
    vector<M> son;
    M init;
    int size, ans;
    vector<int> cnt;

    trie() = default;
    trie(int len) {
        son.reserve(len + 1);
        cnt.reserve(len + 1);
        init.fill(-1);
        size = ans = 0;
        newnode();
    }

    int newnode() {
        son.push_back(init);
        cnt.push_back(0);
        return size++;
    }

    void insert(string &s) {
        int p = 0;
        for(int i = 0; i < s.size(); i++) {
            int u = s[i] - 'a';
            int &nx = son[p][u];
            if(nx == -1) nx = newnode();
            p = nx;
            ans += cnt[p]++;
        }
    }

};

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n;
    cin >> n;
    trie T(3e5);
    for(int i = 0; i < n; i++) {
        string s;
        cin >> s;
        T.insert(s);
    }
    cout << T.ans << endl;
    return 0;
}

F - Tile Distance

大意

给定k,其定义了一个网格:

其中S表示1 \times 1的小格子,L表示k \times k的大格子。

从一个格子走到另一个格子的代价是1

给定起点(sx,sy)和终点(tx,ty),问从起点到终点的最小代价。

思路

显然,最多只能走|sx-tx|+|sy-ty|步,易知这是k=1时的答案。

接下来看k \ge 2的情况。

我们可以知道,如果想要更优的方案,那么需要穿过大格子,否则代价就是上限。

考虑对实现答案的移动中到达的第一对大格子和最后一对大格子进行枚举。

可能到达的第一对大格子为:

  • 如果起点处于大格子,那么就是它。
  • 否则,四周的大格子都有可能。

最后一对同理。

我们分别计算出起终点绕到上述大格子的距离。

剩下的,只需求得在大格子上移动的费用即可。

这就完了吗?并没有。

我们看k=2的情况,发现有时不用绕,直接穿过反而代价更小。

特判一下即可。

代码

#include <iostream>
#include <tuple>
#include <vector>
using namespace std;
typedef unsigned long long ULL;
typedef tuple<ULL, ULL, ULL> P;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    ULL k, sx, sy, tx, ty;
    cin >> k >> sx >> sy >> tx >> ty;

    sx += k, sy += k;
    tx += k, ty += k;

    auto diff = [&](ULL p, ULL q) -> ULL{
        return max(p, q) - min(p, q);
    };

    auto getpos = [&](ULL x, ULL y) -> vector<P> {
        vector<P> pos;
        if(((x / k) ^ (y / k)) & 1)
            pos.emplace_back(x / k, y / k, 0);
        else{
            pos.emplace_back(x / k - 1, y / k, 1 + x % k); // 左
            pos.emplace_back(x / k + 1, y / k, k - x % k); // 右
            pos.emplace_back(x / k, y / k - 1, 1 + y % k); // 下
            pos.emplace_back(x / k, y / k + 1, k - y % k); // 上
        }
        return pos;
    };

    ULL ans = diff(sx, tx) + diff(sy, ty);

    if(1 < k){
        vector<P> start = getpos(sx, sy);
        vector<P> goal = getpos(tx, ty);
        if(k == 2){
            for(auto& [x, y, d1] : start)
                for(auto& [z, w, d2] : goal){
                    ULL x_diff = diff(x, z);
                    ULL y_diff = diff(y, w);
                    ans = min(ans, d1 + d2 + x_diff + y_diff + diff(x_diff, y_diff) / 2);
                }
        }else{
            for(auto& [x, y, d1] : start)
                for(auto& [z, w, d2] : goal)
                    ans = min(ans, d1 + d2 + diff(x + y, z + w) + diff(x + w, z + y));
        }
    }
    cout << ans << endl;
    return 0;
}

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值