求和(NOIP2015)

题目链接:求和

这道题不是很简单,因为数据并不是很小,常规计算会t。

这里引用chenleyu的解答(如果想要cgg原创解答,……改天吧):

这题相对是比较难的,首先我们要解读题目的意思

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

由这段不难得到,所谓纸带,其实就是一个集合组,每一个小集合(格子)里都有三个元素:编号,颜色和上面写的数

就是这样

纸带[]

|num,col,i|num,col,i|num,col,i||num,col,i||num,col,i||num,col,i|

然后我们再来看那神奇的三元组

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

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

2.colorx=colorz

满足上述条件的三元组的分数规定为(x+z)*(number_x+number_z)”

我们看三元组的第一个条件

首先x小于y小于z这很容易理解,我们来看y-x=z-y

通过等式的性质,我们可以将它进行一些变换

y-x=z-y

y+y=z+x

z+x=2y

这样就可以知道z+x必为偶数,那又因为

奇数+奇数=偶数

偶数+偶数=偶数

奇数+偶数=奇数

所以x和z的奇偶性一定相同

所以题目三元组的条件就可以化为

1 z>x

2 z和x的奇偶性相同

3 z格和x格的颜色相同

(跟y一点关系也没有)

换句话说,只要有两个数奇偶性相同,颜色相同,那他们必是会产生分数的

再来看分数的计算

分数=(x+z)*(num[x]+num[z])

我们再来做一点变换

根据乘法分配律

(x+z)*(num[x]+num[z])

=x*num[x]+x*num[z]+z*num[x]+z*num[z]

那假设i1,i2,i3,i4….的奇偶性相同,颜色相同,那么

他们的得分就为

(i1*num[i1]+i1*num[i2]+i2*num[i1]+i2*num[i3])+(i1*num[i1]+i1*num[i3]+i3*num[i1]+i3*num[i3])+…………..

把括号去掉,把和i1有关的放在一起,把和in有关的放在一起

就成了这样

i1*num[i1]+i1*num[i2]+ i1*num[i1]+i1*num[i3]+…….(和i1有关的)

=i1*(num[i1]+num[i2]+…….+num[in])+n*(i1*num[i1])

于是我们就可以得到这个公式

和in有关的得分就

= in*(num[i1]+num[i2]+…….+num[in])+n*(in*num[in])

再把i1,i2,i3….in的得分加起来就是总分了

因为结果很大所以要记得边做边%10007哦

看得出来,他讲得很详细,我相信你也应该能看懂了,下面看我的代码:

#include<bits/stdc++.h>
#ifdef WIN32 //1
#define LL "%I64d"
#else
#define LL "%lld"
#endif
using namespace std;
int n,c;
int sum[2][100001]={0};
int d[2][100001]={0};
int num[100001];
int col[100001];
long long maxx=0;
int main()
{
scanf("%d%d",&n,&c);
for (int i=1;i<=n;i++){
scanf("%d",&num[i]);
num[i]%=10007;
}
int n1=0,n2=0;
for (int i=1;i<=n;i++){
scanf("%d",&col[i]);
sum[i%2][col[i]]+=num[i]; //2
sum[i%2][col[i]]%=10007;
d[i%2][col[i]]++;
}
for (int i=1;i<=n;i++){
maxx+=1LL*i%10007*((sum[i%2][col[i]]+(d[i%2][col[i]]-2)%10007*num[i]+10007)%10007); //3
maxx%=10007;
}
printf(LL,maxx);
return 0;
}

讲三点(这里是cgg原汁原味的讲解):

1处:这里是cgg曾在信息学培训中,提到过的技巧。

2处:这里是计算每一种颜色单数下标和偶数下标的前缀和。

3处:套公式。

上一篇:JAVA递归、非递归遍历二叉树(转)


下一篇:MySQL(四)字段及常用函数