【问题标题】:How to find number of Multiples of 3如何求3的倍数
【发布时间】:2010-09-01 16:22:14
【问题描述】:

这是一个竞赛 Q:

有 N 个数 a[0],a[1]..a[N - 1]。最初都是 0。你必须执行两种类型的操作:

  1. 将索引 A 和 B 之间的数字增加 1。这由命令“0 A B”表示
  2. 回答索引 A 和 B 之间有多少个数可以被 3 整除。这由命令“1 A B”表示。

输入:第一行包含两个整数,N 和 Q。

接下来的每一 Q 行都是上面提到的“0 A B”或“1 A B”的形式。

输出:为“1 A B”形式的每个查询输出 1 行,其中包含相应查询所需的答案。

示例输入:

4 7 1 0 3 0 1 2 0 1 3 1
0 0 0 0 3 1 3 3 1 0 3

样本输出:

4 1 0 2

约束:

1 <= N <= 100000 1 <= Q <= 100000 0 <= A <= B <= N - 1

我不知道如何解决这个问题。你能帮忙吗?

时间限制为 1 秒。我尝试了蛮力,我还尝试为每个 i 保存第 i 个元素之前的 3 除数。

这是我的 C 代码:

#include <stdio.h>


int nums[100*1000+20];
int d[100*1000+20];
int e[100*1000+20];
int dah[100*1000+20];

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    int h;
    for(h=0;h<n;h++)
        {d[h/100]++; e[h/1000]++; dah[h/10]++;}
    int test;
    for(test=0;test<q;test++)
    {
        int op,start,end;
        scanf("%d%d%d",&op,&start,&end);
        if(0==op)
        {
            int x;
            for(x=start;x<=end;x++)
            {
                nums[x]++;
                nums[x]%=3;
                if(nums[x]==0)
                {
                    d[x/100]++;
                    e[x/1000]++;
                    dah[x/10]++;
                }
                else if(nums[x]==1)
                {
                    d[x/100]--;
                    e[x/1000]--;
                    dah[x/10]--;
                }
            }
        }
        else if(1==op)
        {
            int f;
            int ans=0;
            for(f=start;f<=end;)
            {
                if(f%1000==0&&f+1000<end)
                {
                    ans+=e[f/1000];
                    f+=1000;
                }
                else if(f%100==0&&f+100<end)
                {
                    ans+=d[f/100];
                    f+=100;
                }
                else if(f%10==0&&f+10<end)
                {
                    ans+=dah[f/10];
                    f+=10;
                }
                else
                {
                    ans+=(nums[f]==0);
                    f++;
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

在这种方法中,我在 k*1000 和 (k+1)*1000 之间保存了 3 的倍数,对于 k*100 和 (k+1)*100 以及 10 也是如此。这对我有帮助查询更快。但这仍然让我超出了时间限制。

【问题讨论】:

  • 您能否重新格式化以提高可读性?
  • @jer - 作业问题在这里有效且合法。我们通过提供提示而不是完整的答案来回答它们。
  • @Oded:对不起,我是新来的。不知道该怎么办。 @justkt:我尝试了蛮力,还尝试保存每个 i 在第 i 个元素之前有多少个 3 的除数。注意:我目前没有参加比赛。
  • 我忘了说时间限制是1秒
  • @Ali - 我做了一些编辑,看看我所做的更改。另外,请编辑您的问题并添加您作为 cmets 添加的详细信息(1 秒限制,您尝试过的内容)

标签: c algorithm math


【解决方案1】:

提示 #1:

想想你可以如何使用 MODULUS 运算符来帮助你。最初,您有 N 个数字,假设 N 是 5。

所以我们可以存储每个数字的余数(即存储 0 MOD 3、1 MOD 3、2 MOD 3 等):

a[0] = 0
a[1] = 1
a[2] = 2
a[3] = 0
a[4] = 1
a[5] = 2

每次在 A 和 B 之间增加一个数字范围时,您实际上只需要在数组中存储一个 0、1 或 2。例如,如果我们增加 2,那么新数字将是 3。现在可以被 3 整除,所以我们将 0 存储在数组中。所以在我们有 0 并且我们递增的情况下,我们存储 1,如果我们有 1,我们存储 2,如果我们有 2,我们存储 0。

这种优化消除了除初始步骤之外的任何除法的需要。除法是一项非常昂贵的操作,这就是为什么我们要尽可能消除它。

所以在从 0 增加到 5 之后,数组将如下所示:

a[0] = 1
a[1] = 2
a[2] = 0
a[3] = 1
a[4] = 2
a[5] = 0

A 和 B 之间可被 3 整除的数字的数量就是具有 0 的元素的数量(在本例中为 2)。

现在您必须考虑如何有效地查询范围 A 到 B 以找到可被 3 整除的数字的数量。

提示 #2:

要找出区间 [A,B] 内有多少数可以被 3 整除,您可以考虑使用的一种算法/数据结构是分段树。阅读它here。这给您带来的好处是,现在您可以非常快速地计算出任何此类区间 [A,B] 可被 3 整除的数字数量,而不是遍历数组并计算它们。

【讨论】:

  • 感谢您的帮助。但我的问题实际上出在查询中
【解决方案2】:

提示 #3:

dcp 的好建议。虽然它没有透露如何解决问题。 不必将所有数字 MOD 3 存储在数组中。 如果数组中的数字每次都更新,则复杂度为O(Q * N),这对于给定的NQ 显然太多了和 1 秒。在最坏的情况下。这就是Ali 在对dcp 建议的评论中的意义。

段树的每个节点可以存储MOD%0MOD%1MOD%2的整数个数。因此更新可以在O(log N) 中完成,这导致O(Q log N) 仅用于更新。对于每个查询,相同的复杂性 O(log N) 适用。由于您知道每个残基的整数个数 MOD%3,因此无需深入到所有叶(段树中的每个叶对应数组元素)来计算有多少个数可以被 3 整除。一旦你了解了线段树的工作原理,你就会明白为什么需要在线段树的每个节点中存储残差。该算法的总体复杂度为O(Q log N),它非常适合1 sec. time limit

当沿着线段树向下走时,请确保为您在沿着树的途中访问的每个线段累积每个残基的整数数。

【讨论】:

  • 如果我的回答中有任何不清楚的地方,请告诉我。希望你能明白这里的意义所在。
【解决方案3】:

你的数组的上限是多少?首先,弄清楚这一点。然后,计划以这两种形式之一阅读输入行。格式 0 A B 的行很容易处理,你至少能写那么多代码吗?如果是这样,请将其发布,然后担心格式 1 A B 中的行。

【讨论】:

    【解决方案4】:

    如果,正如你的标题所暗示的,你不确定如何判断一个数字是否能被三整除,那么我建议你看看modulus operation,用我最熟悉的语言表示使用%

    【讨论】:

    • 那条评论是多余的,它没有回答问题,我相信作者知道模数运算。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    相关资源
    最近更新 更多