其他分享
首页 > 其他分享> > NOIP 2015 T3求和

NOIP 2015 T3求和

作者:互联网

大概是NOIP 2015 T3求和题解(

 

那么首先发一下题目

 

题目描述

一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。每个格子上都染了一种颜色color_i用[1,m][1,m]当中的一个整数表示),并且写了一个数字number_i。

 

 

 

定义一种特殊的三元组:(x,y,z)(x,y,z),其中x,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:

  1. xyz是整数,x<y<z,y-x=z-y

  2. colorx=colorz

满足上述条件的三元组的分数规定为(x+z)  x (number_x+number_z)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。

 

输入格式

第一行是用一个空格隔开的两个正整数n和m表纸带上格子的个数,m表纸带上颜色的种类数。

第二行有n用空格隔开的正整数,第i数字number表纸带上编号为i格子上面写的数字。

第三行有n用空格隔开的正整数,第i数字color表纸带上编号为i格子染的颜色。

输出格式

一个整数,表示所求的纸带分数除以10007所得的余数。

 

输入#1

6 2

5 5 3 2 2 2 

2 2 1 1 2 1

 

输出#1

82

 

输入#2

15 4 

5 10 8 2 2 2 9 9 7 7 5 6 4 2 4

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

 

输出#2

1388

 

 

那么下面开始分析环节:

一   分析题目

题目很好懂

1.首先理解三元组要求

    1. xyz是整数,x<y<z,y-x=z-y

    2. colorx=colorz

2.找到三元组后的处理

即是累加(x+z)  x (number_x+number_z)

 

二 思考初步解题策略并观察数据范围

首先一步变形:y-x=z-y可以变成  2y=x+z

数学好的小伙伴可能会发现这是个等差中项,嗯不过和这个题没啥关系(

 

第一想法,既然要满足三元组要求的话,直接枚举x,y,z不就好了嘛。枚举y点,再在内层循环寻找x点与z点,然后再特判颜色以及是否满足2y=x+z,找到后累加即可

复杂度O(n^3)得分20

 

第二想法 看了看数据范围后发现O(n^3)的算法太扯了,考虑优化。我们发现,当2y=x+z时,两边都时mod一个2后,可以知道x+zΞ0(mod2)(翻译成计算机语言就是(x+z)%2==0)这样就是一个满足条件的三元组。有些同学这里可能会疑惑,认为这样不够严谨,其实这样是合理的。因为在满足x<y<z时,若x z确定,那么y必定有存在性。而且我们对于求和的处理中也没有牵扯y的计算,所以直接枚举x z,然后特判一下同奇偶性以及颜色,再处理求和就可以了。

复杂度O(n^2)得分 40

 

三 最终正解想法以及推理流程

其实AC前两题加上O(n^2)的算法后已经达到了240的高分,那么看到这里的一定是力求AK的大佬选手了。

在介绍完O(n^2)的算法后发现还是欠优异,此时我们注意数据范围

n可以开到10万,数据毒以及优化难想到是这个题的最大难点(毕竟是个绿题起码还是要点面子的嘛)

我们有了第二想法的关于奇偶性判定这一思路后,考虑O(n)算法。

我们发现当有 A1 A2 A3.....An这n个数满足互相形成三元组后,这里注意是“互相“,也就是A1A2 A2A3 A1A3....

不妨考虑这几个数处理后加和的值

这里是公式以及证明:

 

 得出如此结论后就非常简单了,那么我们只要找出符合奇偶性,以及符合同颜色性质的数然后统一套到这个公式里求和一波就可以了。

 

 

 四  预处理的技巧以及完美代码的呈现

显然,我们要做一波预处理。

这里呢我的数组sum[2][1000000]前面的维度判断奇偶性质,后面的维度呢就是判断颜色性质,这个数组用来存储Σnumber[i]。

然后我又定义了个vis[2][100000]数组,作用是在此奇偶性以及颜色性质判断下,元素的个数。

预处理代码如下

    for(int i=1;i<=n;i++)
    {
        sum[i%2][color[i]]+=number[i];
        sum[i%2][color[i]]%=Mod;
        vis[i%2][color[i]]++;
    }

最后根据我们前面的公式推理得出最终的处理求和代码

    for(int i=1;i<=n;i++)
{
    ans+=((i*(vis[i%2][color[i]]-2)*number[i])%Mod+(sum[i%2][color[i]]*i))%Mod;
    ans%=Mod;
}

这样,这个题基本就写完了,剩下的工作就是读入了。

 

完整代码呈现:

复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#define Mod 10007
#define N 100007
long long ans,sum[2][N],color[N],number[N];
int vis[2][N];
using namespace std;
int main()
{
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>number[i];
        number[i]%=Mod;
    }
    for(int j=1;j<=n;j++)
    {
        cin>>color[j];
    }
    for(int i=1;i<=n;i++)
    {
        sum[i%2][color[i]]+=number[i];
        sum[i%2][color[i]]%=Mod;
        vis[i%2][color[i]]++;
    }
    
    for(int i=1;i<=n;i++)
{
    ans+=((i*(vis[i%2][color[i]]-2)*number[i])%Mod+(sum[i%2][color[i]]*i))%Mod;
    ans%=Mod;
}

cout<<ans;
return 0;    
 } 
复制代码

 

 

五 个人的看法已经一些其他的话(

这个题我想花了10min,毕竟因式分解啥的对于我这种数学竞赛生来说本身不是难事,码的话花了20min,基本在于思考预处理问题上了。这个题我认为的巧妙之处不在于因式分解以及公式推理,在于预处理的巧妙运用以及一次次考虑优化的过程。从O(n^3)到O(n)的一次次思维跃进。解决一个题不是目的,学会了这样的优化思维以及预处理的技巧,我想,这才是这个题的真正意义。

 

然后呢对于历城二中信息奥赛队中较多队员AC了这道题,其中还有位五年级的同学并且是此次测试的Rank1。

我这一初三老头还那么菜......(羞愧脸)五年级就已熟练运用因式分解以及求和符号属实强壮。最后呢祝愿历城二中信息队的队员携手并进越来越强,同时也祝广大oier能在NOIP2020中取得满意的成绩。

 

 

如果对您有帮助的话可以给个赞嘛qwq

 

大概是NOIP 2015 T3求和题解(

 

那么首先发一下题目

 

题目描述

一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。每个格子上都染了一种颜色color_i用[1,m][1,m]当中的一个整数表示),并且写了一个数字number_i。

 

 

 

定义一种特殊的三元组:(x,y,z)(x,y,z),其中x,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:

  1. xyz是整数,x<y<z,y-x=z-y

  2. colorx=colorz

满足上述条件的三元组的分数规定为(x+z)  x (number_x+number_z)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。

 

输入格式

第一行是用一个空格隔开的两个正整数n和m表纸带上格子的个数,m表纸带上颜色的种类数。

第二行有n用空格隔开的正整数,第i数字number表纸带上编号为i格子上面写的数字。

第三行有n用空格隔开的正整数,第i数字color表纸带上编号为i格子染的颜色。

输出格式

一个整数,表示所求的纸带分数除以10007所得的余数。

 

输入#1

6 2

5 5 3 2 2 2 

2 2 1 1 2 1

 

输出#1

82

 

输入#2

15 4 

5 10 8 2 2 2 9 9 7 7 5 6 4 2 4

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

 

输出#2

1388

 

 

那么下面开始分析环节:

一   分析题目

题目很好懂

1.首先理解三元组要求

    1.xyz是整数,x<y<z,y-x=z-y

    2.colorx=colorz

2.找到三元组后的处理

即是累加(x+z)  x (number_x+number_z)

 

二 思考初步解题策略并观察数据范围

首先一步变形:y-x=z-y可以变成  2y=x+z

数学好的小伙伴可能会发现这是个等差中项,嗯不过和这个题没啥关系(

 

第一想法,既然要满足三元组要求的话,直接枚举x,y,z不就好了嘛。枚举y点,再在内层循环寻找x点与z点,然后再特判颜色以及是否满足2y=x+z,找到后累加即可

复杂度O(n^3)得分20

 

第二想法 看了看数据范围后发现O(n^3)的算法太扯了,考虑优化。我们发现,当2y=x+z时,两边都时mod一个2后,可以知道x+zΞ0(mod2)(翻译成计算机语言就是(x+z)%2==0)这样就是一个满足条件的三元组。有些同学这里可能会疑惑,认为这样不够严谨,其实这样是合理的。因为在满足x<y<z时,若x z确定,那么y必定有存在性。而且我们对于求和的处理中也没有牵扯y的计算,所以直接枚举x z,然后特判一下同奇偶性以及颜色,再处理求和就可以了。

复杂度O(n^2)得分 40

 

三 最终正解想法以及推理流程

其实AC前两题加上O(n^2)的算法后已经达到了240的高分,那么看到这里的一定是力求AK的大佬选手了。

在介绍完O(n^2)的算法后发现还是欠优异,此时我们注意数据范围

n可以开到10万,数据毒以及优化难想到是这个题的最大难点(毕竟是个绿题起码还是要点面子的嘛)

我们有了第二想法的关于奇偶性判定这一思路后,考虑O(n)算法。

我们发现当有 A1 A2 A3.....An这n个数满足互相形成三元组后,这里注意是“互相“,也就是A1A2 A2A3 A1A3....

不妨考虑这几个数处理后加和的值

这里是公式以及证明:

 

 得出如此结论后就非常简单了,那么我们只要找出符合奇偶性,以及符合同颜色性质的数然后统一套到这个公式里求和一波就可以了。

 

 

 四  预处理的技巧以及完美代码的呈现

显然,我们要做一波预处理。

这里呢我的数组sum[2][1000000]前面的维度判断奇偶性质,后面的维度呢就是判断颜色性质,这个数组用来存储Σnumber[i]。

然后我又定义了个vis[2][100000]数组,作用是在此奇偶性以及颜色性质判断下,元素的个数。

预处理代码如下

    for(int i=1;i<=n;i++)
    {
        sum[i%2][color[i]]+=number[i];
        sum[i%2][color[i]]%=Mod;
        vis[i%2][color[i]]++;
    }

最后根据我们前面的公式推理得出最终的处理求和代码

    for(int i=1;i<=n;i++)
{
    ans+=((i*(vis[i%2][color[i]]-2)*number[i])%Mod+(sum[i%2][color[i]]*i))%Mod;
    ans%=Mod;
}

这样,这个题基本就写完了,剩下的工作就是读入了。

 

完整代码呈现:

复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#define Mod 10007
#define N 100007
long long ans,sum[2][N],color[N],number[N];
int vis[2][N];
using namespace std;
int main()
{
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>number[i];
        number[i]%=Mod;
    }
    for(int j=1;j<=n;j++)
    {
        cin>>color[j];
    }
    for(int i=1;i<=n;i++)
    {
        sum[i%2][color[i]]+=number[i];
        sum[i%2][color[i]]%=Mod;
        vis[i%2][color[i]]++;
    }
    
    for(int i=1;i<=n;i++)
{
    ans+=((i*(vis[i%2][color[i]]-2)*number[i])%Mod+(sum[i%2][color[i]]*i))%Mod;
    ans%=Mod;
}

cout<<ans;
return 0;    
 } 
复制代码

 

 

五 个人的看法已经一些其他的话(

这个题我想花了10min,毕竟因式分解啥的对于我这种数学竞赛生来说本身不是难事,码的话花了20min,基本在于思考预处理问题上了。这个题我认为的巧妙之处不在于因式分解以及公式推理,在于预处理的巧妙运用以及一次次考虑优化的过程。从O(n^3)到O(n)的一次次思维跃进。解决一个题不是目的,学会了这样的优化思维以及预处理的技巧,我想,这才是这个题的真正意义。

 

然后呢对于历城二中信息奥赛队中较多队员AC了这道题,其中还有位五年级的同学并且是此次测试的Rank1。

我这一初三老头还那么菜......(羞愧脸)五年级就已熟练运用因式分解以及求和符号属实强壮。最后呢祝愿历城二中信息队的队员携手并进越来越强,同时也祝广大oier能在NOIP2020中取得满意的成绩。

 

 

如果对您有帮助的话可以给个赞嘛qwq

 

标签:格子,NOIP,int,纸带,number,T3,2015,三元组,预处理
来源: https://www.cnblogs.com/gclqwq/p/12791184.html