AtCoder Regular Contest 084

Snuke Festiv

题意:

给定长度为 N N N的三个数组 A,B,C,现在让你分别从A,B,C中各选一个数,要保证A中选的数严格小于从B中选的数,从B中选的数要严格小于从C中选的数,即 A i < B j < C k A_i<B_j<C_k Ai<Bj<Ck,问有多少种选法?

思路:

先枚举每个 B j B_j Bj,算出每个 B j B_j Bj能搭配多少个 C k C_k Ck,这个用二分找即可,然后把这些数记录下来,求一边后缀和 s u m j sum_j sumj,然后再枚举每个 A i A_i Ai算出有多少个 B j B_j Bj符合要求,然后算出 B j + B j + 1 + . . . . B N B_j+B_{j+1}+....B_N Bj+Bj+1+....BN的和,这个和就是前面提前预处理出来的后缀和,然后累加即可

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
#define ll long long
int a[N], b[N], c[N];
int n;
ll res;
ll sum[N];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    sort(c + 1, c + n + 1);
    for (int i = 1; i <= n; i++) {
        int now = b[i];
        int t = upper_bound(c + 1, c + n + 1, now) - c;
        sum[i] = n - t + 1ll;
    }
    for (int i = n; i >= 1; i--) {
        sum[i] += sum[i + 1];
    }
    for (int i = 1; i <= n; i++) {
        int now = a[i];
        int t = upper_bound(b + 1, b + n + 1, now) - b;
        res += sum[t];
    }
    cout << res << endl;
}

Small Multiple

题意:

给定一个函数 f ( n ) f(n) f(n) n n n的各位数字之和,现在给定一个 n n n,让你算出 n n n的所有倍数中,所有 k n kn kn f ( k n ) f(kn) f(kn)的最小值

思路:

单纯的枚举所有的 n n n的倍数,在把各位数求和,最后取最小值,一定会超时,数量范围太大。换个思路,去看一个数的各个位数之和是如何算出来的。
先仅看个位的话, 1 1 1的位数和位 1 1 1 2 2 2就是 1 + 1 1+1 1+1,… 9 9 9就是 1 + 8 1+8 1+8
再看十位的话,要想快速形成 10 10 10,一定是 10 × 1 10×1 10×1而不是 1 + 9 1+9 1+9

在这里插入图片描述
会发现在这个二叉树结构中, + 1 +1 +1代表着各位数和 + 1 +1 +1(在不进位的情况下), × 10 ×10 ×10代表着各位数和不变,所以在这个树形结构中,我们只要找到各位数最小的且这个数是 n n n的倍数的数,就是答案。在这个树形结构中,重复出现的数的就拿最小的各位数和处理即可。此时只要构造出这个树形结构即可, + 1 +1 +1代表边权为 1 1 1 × 10 ×10 ×10代表边权为 0 0 0,只要在搜索过程中第一次出现 n n n的倍数,那么这个数就是答案,对于这个搜索结构,边权只有 0 和 1 0和1 01,所以就可以用双端队列广搜即可,也可以建边最短路,是一样的效果。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define int long long
typedef pair<int,int> PII;
bool vis[N];
int k;
int BFS() {
    deque<PII> q;
    q.push_back({1ll,1ll});
    
    while(q.size()) {
        auto t=q.front();
        q.pop_front();
        int now=t.first;
        int step=t.second;
        if(vis[now]) continue;
        vis[now]=1;
        if(now==0) {
            return step;
        }
        if(!vis[(now+1)]) {
            q.push_back({(now+1),step+1});
            //vis[(now+1)]=1;
        } 
        if(!vis[(10*now)%k]) {
            q.push_front({(10*now)%k,step});
            //vis[(10*now)%k]=1;
        }
    }
    return 0;
}
signed main() {
    cin>>k;
    int res=BFS();
    cout<<res<<endl;
}

Finite Encyclopedia of Integer Sequences.

题意:

现在会有很多个序列长度在 1 到 N 1到N 1N之间,且序列内的数字大小在 1 到 K 1到K 1K之间的数,总共会出现 X X X个序列(未知的),现在在按照字典序排完序后,让你输出位于 ⌈ X / 2 ⌉ ⌈X/2⌉ X/2的序列

思路:

对于 K K K为偶数的情况,可以发现以每个数字为开头的序列的总数都是一样的,所以以取就是相当于 K K K个数中取第 ⌈ K / 2 ⌉ ⌈K/2⌉ K/2号数一样,因为是整个序列的中间,所以一定是以 K / 2 K/2 K/2为开头的序列当中,后面的 N − 1 N-1 N1个数都是 K K K的情况。
对于 K K K为奇数的情况,因为以每个数字为开头的序列的总数都是一样的,所以答案序列的开头一定是 ⌈ K / 2 ⌉ ⌈K/2⌉ K/2,由于有长度小于 N N N的序列在前面干扰,所以不好做判断,先假设当前这个数引导的只有以长度为 N N N的序列,那么此时,其中间序列一定是 K + 1 2 K + 1 2 K + 1 2 . . . . . . . \frac{K+1}{2} \frac{K+1}{2} \frac{K+1}{2}....... 2K+12K+12K+1....... N N N个。由于大于 K + 1 2 \frac{K+1}{2} 2K+1的数和小于 K + 1 2 \frac{K+1}{2} 2K+1的数的数量是一样的,所以就代表着这些数都有对标的比它大的数或者比它小的数,例如,
2 2 2
21 21 21 211 211 211 212 212 212 213 213 213
22 22 22 221 221 221 222 222 222 223 223 223
23 23 23 231 231 231 232 232 232 233 233 233
21 21 21 对标 23 23 23 211 211 211 对标 231 231 231
会发现只有 2 2 2 22 22 22没有对标的数,也就是说,对答案有影响的序列就是那些仅有 K + 1 2 \frac{K+1}{2} 2K+1组成的序列,但长度小于 N N N,总共有 N − 1 N-1 N1个,要找到的是中位序列,所以要保证前面的数的数量等于后面的数的数量,所以要往前移动 ⌈ N − 1 2 ⌉ ⌈\frac{N-1}{2}⌉ 2N1个位置,就是中位序列。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
#define ll long long
int k, n;
int a[N];
int main() {
    cin >> k >> n;
    if (k % 2 == 0) {
        printf("%d", k / 2);
        for (int i = 2; i <= n; i++) {
            printf(" %d", k);
        }
    } else {
        for (int i = 1; i <= n; i++) {
            a[i] = (k + 1) / 2;
        }
        int now = n;
        for (int i = 1; i <= n / 2; i++) {
            if (a[now] == 1) {
                now--;
            } else {
                a[now]--;
                for (int j = now + 1; j <= n; j++) {
                    a[j] = k;
                }
                now = n;
            }
        }
        for (int i = 1; i <= now; i++) {
            printf("%d ", a[i]);
        }
    }
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值