NOIP2015 T3 求和 乱作题解
题目
题目背景
NOIP2015 普及组 T3
题目描述
一条狭长的纸带被均匀划分出了\(n\)个格子,格子编号从\(1\)到\(n\)。每个格子上都染了一种颜色\(color_i\)用\([1,m]\)当中的一个整数表示),并且写了一个数字\(number_i\)。

定义一种特殊的三元组:\((x,y,z)\),其中\(x,y,z\)都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:
-
\(xyz\)是整数,\(x<y<z,y-x=z-y\)
-
\(colorx=colorz\)
满足上述条件的三元组的分数规定为\((x+z) \times (number_x+number_z)\)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以\(10,007\)所得的余数即可。
输入格式
第一行是用一个空格隔开的两个正整数\(n\)和\(m,n\)表纸带上格子的个数,\(m\)表纸带上颜色的种类数。
第二行有\(n\)用空格隔开的正整数,第\(i\)数字\(number\)表纸带上编号为\(i\)格子上面写的数字。
第三行有\(n\)用空格隔开的正整数,第\(i\)数字\(color\)表纸带上编号为\(i\)格子染的颜色。
输出格式
一个整数,表示所求的纸带分数除以\(10007\)所得的余数。
样例 #1
样例输入 #1
6 2
5 5 3 2 2 2
2 2 1 1 2 1
样例输出 #1
82
样例 #2
样例输入 #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, 3, 5), (4, 5, 6)\)。
所以纸带的分数为\((1 + 5) \times (5 + 2) + (4 + 6) \times (2 + 2) = 42 + 40 = 82\)。
对于第 \(1\) 组至第 \(2\) 组数据, \(1 ≤ n ≤ 100, 1 ≤ m ≤ 5\);
对于第$ 3$ 组至第 \(4\) 组数据, \(1 ≤ n ≤ 3000, 1 ≤ m ≤ 100\);
对于第 \(5\) 组至第$ 6 $组数据, \(1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000\),且不存在出现次数超过\(20\)的颜色;
对 于 全 部 \(10\) 组 数 据 , \(1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, 1 ≤ color_i ≤ m,1≤number_i≤100000\)
看起来好像不是很难啊,为什么我做不出来呢;
1. 暴力枚举
枚举x,y,z的值,再判断是否符合条件 ;
时间复杂度: \(\mathcal{O}(n ^ 3)\)
期望得分:\(20pts\)
\(Code\):
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
long long n,m,ans;
long long color[N],number[N];
int main()
{
scanf("%lld %lld",&n , &m);
for(int i=1 ; i<=n ; i++) scanf("%lld",&number[i]);
for(int i=1 ; i<=n ; i++) scanf("%lld",&color[i]);
for(int x=1 ; x<=n ; x++)
{
for(int y=x+1 ; y<=n ; y++)
{
for(int z=y+1 ; z<=n ; z++)
{
if( color[x] == color[z] && y-x == z-y )
ans += (x+z) * (number[x] + number[z]);
}
}
}
printf("%lld",ans%10007);
}
2.优化暴力枚举
整理条件(\(y-z=z-y\))我们可以发现
$$y = (z+x)\div2$$
也就是说,我们可以通过枚举x,z的值来确定y的值
时间复杂度: \(\mathcal{O}(n ^ 2)\)
期望得分:\(40pts\)
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
long long n,m,ans;
long long color[N],number[N];
int main()
{
scanf("%lld %lld",&n , &m);
for(int i=1 ; i<=n ; i++) scanf("%lld",&number[i]);
for(int i=1 ; i<=n ; i++) scanf("%lld",&color[i]);
for(int x=1 ; x<=n ; x++)
{
for(int z=x+2 ; z<=n ; z+=2)
{
int y=(x+z)/2;
if(color[x] == color[z])
ans += (x+z) * (number[x] + number[z]);
}
}
printf("%lld",ans%10007);
}
3.正解
仔细观察这个式子 $$y = (x+z)\div2$$
因为\(y\)是整数
所以\(x+z\)必定是偶数
也就是说 \(x\)和\(z\)同奇偶
再想想题目(\(color[x]=color[z]……\)
◝(⑅•ᴗ•⑅)◜..°♡ 欸!!瞧瞧我们发现了什么!
\(x\)和\(z\)具有两项相同的性质
整理一下
- \(x与z同奇偶\)
- \(color[x] = color[z]\)
这个时候我们就要运用分组思想
————那么 什么是分组思想?
分组思想,就是把具有相同性质的两个集合放在一起,方便计算。
————沃兹·基朔德
放在这题里就是
先将所有数字按颜色分组,再按奇偶分组(其实先按什么分都一样的啦
显然,每一组中,任意一对x,z都可以构造出一个符合条件的三元组(\(y=(x+z)\div2\))
推式子时间到!
我们不知道从哪搞来了两个数组a,number;
- a[i]表示某一组内第i个格子的编号
- number[i]表示某一组内第i个格子表上写的数字
- k表示组内共有k个格子
那么这个组内的分数应该为
化简一下可得
至于计算\(number[1]+number[2]+……+number[k]\)只需要加一个前缀和优化就可以力
那么整张纸条的分数就是将所有分出来的组的分数的总和!!!
有了思路,写代码清晰无比;
时间复杂度:\(\mathcal{O}(n ^ )\)
期望得分:\(100pts\)
\(Code\)
#include <bits/stdc++.h>
using namespace std;
const int NN=100010;
const int MOD = 10007;
long long number[NN] ,color[NN];
long long sum[NN][2] , k[NN][2];//sum是前缀和数组,k是每组的个数
long long n,m,ans;
int main()
{
scanf("%d %d" , &n , &m);
for(int i=1 ; i<=n ; i++) scanf("%d",&number[i]);
for(int i=1 ; i<=n ; i++) scanf("%d",&color[i]);
for(int i=1 ; i<=n ; i++)
{
k[color[i]][i%2] ++;
sum[color[i]][i%2] = (sum[color[i]][i%2] + number[i]) % MOD;
}
for(int i=1 ; i<=n ; i++)
{
ans += i * (number[i] * (k[color[i]][i%2]-2) + sum[color[i]][i%2] );
ans %= MOD;
}
printf("%d",ans);
return 0;
}
By Hu_taooo

浙公网安备 33010602011771号