AcWing - 寒假每日一题2023(DAY 6——DAY 10)

一、AcWing 4645. 选数异或(中等)

题目描述

给定一个长度为 n 的数列 A1,A2,⋅⋅⋅,An 和一个非负整数 x,给定 m 次查询,每次询问能否从某个区间 [l,r] 中选择两个下标不同的数使得他们的异或等于 x。

输入格式

输入的第一行包含三个整数 n,m,x。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An
接下来 m 行,每行包含两个整数 li,ri 表示询问区间 [li,ri]。

输出格式

对于每个询问,如果该区间内存在两个数的异或为 x 则输出 yes,否则输出 no

数据范围

对于 20% 的评测用例,1 ≤ n,m ≤ 100
对于 40% 的评测用例,1 ≤ n,m ≤ 1000
对于所有评测用例,1 ≤ n,m ≤ 100000,0 ≤ x < 220,1 ≤ li ≤ ri ≤ n,0 ≤ Ai <220

输入样例

4 4 1
1 2 3 4
1 4
1 2
2 3
3 3

输出样例

yes
no
yes
no

样例解释

显然整个数列中只有 2,3 的异或为 1。

具体实现

1. 实现思路

  • 我们有 n 个数,然后在 n 个数当中进行 m 次询问(判断),每次询问给一个区间,判断这个区间是否有一对数的异或值等于 x,有的话输出 yes,没有的话输出 no
  • 由于 n,m 的数据范围是100000,因此,我们需要将时间复杂度控制在 O(nlogn)。
  • 首先发现 a ^ b = x -> a ^ x = b -> b ^ x = a,也就是我们只要先把 x 跟每个数的异或值 b 提前算出,如果我们知道的了 b,那么只要 b 在 l ~ r 的范围内就符合题意了。这时输出 Yes 即可。
  • 但是,直接暴力判断的话,要两层 for 循环,时间复杂度时 O(n2),不符合要求。
  • 如果在 l 到 r 当中存在,那么一定在 1 到 r 当中存在(用于缩减询问次数)。
  • 假设,fi 是在 ai 左边,与 ai 配对的最近的一个数,那么 fi 一定是小于 r 的,我们只需要判断 fi 是否大于 l 即可。因此,我们只需要寻找这个 i 。
  • 为了简便,我们可以设定一个数组 g[i] 用于表示 i 左边 fi 的最大值,只需要判断 g® 是否大于等于 l。

2. 实现代码

#include <bits/stdc++.h>

using namespace std;

// 1<<20 表示2的20次方,+10是为了防止溢出
const int N = 100010, M = (1 << 20) + 10; 

int n, m, x;
//last[M]表示每个数最后一次出现的位置
//g[N]表示用于表示 i 左边 f~i~ 的最大值
int last[M], g[N];

int main()
{
    cin >> n >> m >> x;
    for (int i = 1; i <= n; i ++ )
    {
        int a;
        cin >> a;

        //如果一个数字没有出现过,last数组是全局变量,就是0
        //递归
        g[i] = max(g[i - 1], last[a ^ x]);
        //更新last
        last[a] = i;
    }

    while (m -- )
    {
        int l, r;
        cin >> l >> r;
        if (g[r] >= l) 
        {
            puts("yes");
        }
        else
        {
            puts("no");
        }
    }
    
    system("pause");
    return 0;
}

二、AcWing 4644. 求和(简单)

题目描述

给定 n 个整数 a1,a2,⋅⋅⋅,an,求它们两两相乘再相加的和,即
S=a1⋅a2+a1⋅a3+⋅⋅⋅+a1⋅an+a2⋅a3+⋅⋅⋅+an−2⋅an−1+an−2⋅an+an−1⋅an

输入格式

输入的第一行包含一个整数 n。
第二行包含 n 个整数 a1,a2,⋅⋅⋅,an

输出格式

输出一个整数 S,表示所求的和。
请使用合适的数据类型进行运算。

数据范围

对于 30% 的数据,1 ≤ n ≤ 1000,1 ≤ ai ≤ 100。
对于所有评测用例,1 ≤ n ≤ 200000,1 ≤ ai ≤ 1000。

输入样例

4
1 3 6 9

输出样例

117

具体实现

1. 实现思路

  • 如果直接采取暴力做法两个 for 循环直接会超时,因此,需要进行优化。
  • 可以使用前缀和或者推导公式两种方法,递推公式整体比较复杂,在此不做过多叙述。
  • 前缀和详见博文 基础算法-前缀和

2. 实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 200010;

int n;
int a[N];

int main()
{
    cin >> n;

    long long sum = 0;
    for(int i = 1; i <= n; i ++ )
    {
        cin >> a[i];
        sum += a[i];
    }

    long long res = 0, cnt = 0;   
    for(int i = 1; i <= n; i ++ )
    {
        cnt += a[i];
        res += a[i] * (sum - cnt);
    }

    cout << res << endl;
    system("pause");
    return 0;
}

三、AcWing 4653. 数位排序(简单)

题目描述

小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。
当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。
例如,2022 排在 409 前面,因为 2022 的数位之和是 6,小于 409 的数位之和 13。
又如,6 排在 2022 前面,因为它们的数位之和相同,而 6 小于 2022。
给定正整数 n,m,请问对 1 到 n 采用这种方法排序时,排在第 m 个的元素是多少?

输入格式

输入第一行包含一个正整数 n。
第二行包含一个正整数 m。

输出格式

输出一行包含一个整数,表示答案。

数据范围

对于 30% 的评测用例,1 ≤ m ≤ n ≤ 300。
对于 50% 的评测用例,1 ≤ m ≤ n ≤ 1000。
对于所有评测用例,1 ≤ m ≤ n ≤ 106

输入样例

13
5

输出样例

3

样例解释

1 到 13 的排序为:1,10,2,11,3,12,4,13,5,6,7,8,9。
第 5 个数为 3。

具体实现

1. 实现思路

  • 不可以实时计算每一个数字的数位和,有可能会导致超时。
  • 因此,我们可以使用一个数组,将每个数的数位之和存储进去,然后使用快速排序即可。
  • 快速排序详见 基础算法-快速排序

2. 实现代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1000010;

int n, m;
int w[N], s[N];

bool cmp(int a, int b)
{
    if (s[a] != s[b]) 
    {
        return s[a] < s[b];
    }
    return a < b;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
    {
        w[i] = i;
        for (int j = i; j != 0; j /= 10)
        {
            s[i] += j % 10;
        }
    }

    sort(w + 1, w + n + 1, cmp);

    cout << w[m] << endl;
    system("pause");
    return 0;
}

四、AcWing 4655. 重新排序(中等)

题目描述

给定一个数组 A 和一些查询 Li,Ri,求数组中第 Li 至第 Ri 个元素之和。
小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。
小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少?

输入格式

输入第一行包含一个整数 n。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An,相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m 表示查询的数目。
接下来 m 行,每行包含两个整数 Li、Ri,相邻两个整数之间用一个空格分隔。

输出格式

输出一行包含一个整数表示答案。

数据范围

对于 30% 的评测用例,n,m ≤ 50;
对于 50% 的评测用例,n,m ≤ 500;
对于 70% 的评测用例,n,m ≤ 5000;
对于所有评测用例,1 ≤ n,m ≤ 105,1 ≤ Ai ≤ 106,1 ≤ Li ≤ Ri ≤ n。

输入样例

5
1 2 3 4 5
2
1 3
2 5

输出样例

4

样例解释

原来的和为 6+14=20,重新排列为 (1,4,5,2,3) 后和为 10+14=24,增加了 4。

具体实现

1. 实现思路

  • 首先,我们可以先统计一下每一位数在答案当中会加多少次。
  • 具体方法可以通过差分实现,当 l 到 r 这段区间被求和时,就 将 bl 加上 1,br+1 减去 1 就可以了。具体详见 基础算法-差分
  • 然后,重新排列,使得最大的相互对应,按照顺序去配对。
  • 求和的过程就是前缀和的步骤,具体详见 基础算法-前缀和

2. 实现代码

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 100010;

int n, m;
//w[N]表示原数组
//s[N]表示记录被加次数的数组
int w[N], s[N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> w[i];
    }

    cin >> m;
    while (m -- )
    {
        int l, r;
        cin >> l >> r;
        s[l] ++, s[r + 1] -- ;
    }

    //对原数组求前缀和
    for (int i = 1; i <= n; i ++ )
    {
        s[i] += s[i - 1];
    }

    //最初的总和
    LL sum1 = 0;
    for (int i = 1; i <= n; i ++ )
    {
        sum1 += (LL)s[i] * w[i];
    }

    //重新排序后的总和
    LL sum2 = 0;
    sort(s + 1, s + n + 1);
    sort(w + 1, w + n + 1);
    for (int i = 1; i <= n; i ++ )
    {
        sum2 += (LL)s[i] * w[i];
    }

    cout << sum2 - sum1 << endl;
    system("pause");
    return 0;
}

五、AcWing 4652. 纸张尺寸(简单)

题目描述

在 ISO 国际标准中定义了 A0 纸张的大小为 1189mm×841mm,将 A0 纸沿长边对折后为 A1 纸,大小为 841mm×594mm,在对折的过程中长度直接取下整(实际裁剪时可能有损耗)。
将 A1 纸沿长边对折后为 A2 纸,依此类推。
输入纸张的名称,请输出纸张的大小。

输入格式

输入一行包含一个字符串表示纸张的名称,该名称一定是 A0、A1、A2、A3、A4、A5、A6、A7、A8、A9 之一。

输出格式

输出两行,每行包含一个整数,依次表示长边和短边的长度。

输入样例 1

A0

输出样例 1

1189
841

输入样例 2

A1

输出样例 2

841
594

具体实现

1. 实现思路

  • 只需要依次递推计算即可,中间需要注意长边和短边的变换。
  • 当 x 小于 y 的时候,下一次就是 y 折半,为了方便,这里直接将 x 与 y 交换即可。

2. 实现代码

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    char s;
    cin >> s >> n;
    int x = 1189, y = 841;
    while (n -- )
    {
        x /= 2;
        if (x < y)
        {
            swap(x, y);
        }
    }

    cout << x << endl << y << endl;
    system("pause");
    return 0;
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚心求知的熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值