NOIP2015 T3 求和 乱作题解

题目

题目背景

NOIP2015 普及组 T3

题目描述

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

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

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

  2. \(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个格子

那么这个组内的分数应该为

\[(a[1]+a[2])\times(number[1]+number[2])+(a[1]+a[3])\times(number[1]+number[3])+……+(a[2]+a[3])\times(numebr[2]+number[3]) \]

\[+(a[2]+a[4])\times(number[2]+number[4])+……+(a[k-1]+a[k])\times(number[k-1]+number[k]) \]

化简一下可得

\[a[1]\times(number[1]+number[2]+number[1]+number[3]……+number[1]+number[k])+……+a[k]\times \]

\[(number[1]+number[k]+number[2]+number[k]+……+number[k-1]+number[k]) \]

\[=a[1]\times(number[1]\times(k-2)+number[1]+number[2]+……+number[k])+………+a[k]\times \]

\[(number[k]\times(k-2)+number[1]+number[2]+……+number[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

posted @ 2022-10-06 12:54  Hu_taooo  阅读(268)  评论(3)    收藏  举报