NOIP2015 T3 求和 乱作题解

题目

题目背景

NOIP2015 普及组 T3

题目描述

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

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

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

  2. colorx=colorz

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

输入格式

第一行是用一个空格隔开的两个正整数nm,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)×(5+2)+(4+6)×(2+2)=42+40=82

对于第 1 组至第 2 组数据, 1n100,1m5

对于第3 组至第 4 组数据, 1n3000,1m100

对于第 5 组至第6组数据, 1n100000,1m100000,且不存在出现次数超过20的颜色;

对 于 全 部 10 组 数 据 , 1n100000,1m100000,1colorim,1numberi100000

看起来好像不是很难啊,为什么我做不出来呢

1. 暴力枚举

枚举x,y,z的值,再判断是否符合条件 ;
时间复杂度: O(n3)
期望得分: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.优化暴力枚举

整理条件(yz=zy)我们可以发现
y=(z+x)÷2
也就是说,我们可以通过枚举x,z的值来确定y的值
时间复杂度: O(n2)
期望得分: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)÷2
因为y是整数
所以x+z必定是偶数
也就是说 xz同奇偶
再想想题目(color[x]=color[z]

◝(⑅•ᴗ•⑅)◜..°♡ 欸!!瞧瞧我们发现了什么!

xz具有两项相同的性质
整理一下

  • xz
  • color[x]=color[z]

这个时候我们就要运用分组思想
————那么 什么是分组思想?

分组思想,就是把具有相同性质的两个集合放在一起,方便计算。
						————沃兹·基朔德

放在这题里就是
先将所有数字按颜色分组,再按奇偶分组(其实先按什么分都一样的啦

显然,每一组中,任意一对x,z都可以构造出一个符合条件的三元组(y=(x+z)÷2

推式子时间到!

我们不知道从哪搞来了两个数组a,number;

  • a[i]表示某一组内第i个格子的编号
  • number[i]表示某一组内第i个格子表上写的数字
  • k表示组内共有k个格子

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

(a[1]+a[2])×(number[1]+number[2])+(a[1]+a[3])×(number[1]+number[3])++(a[2]+a[3])×(numebr[2]+number[3])

+(a[2]+a[4])×(number[2]+number[4])++(a[k1]+a[k])×(number[k1]+number[k])

化简一下可得

a[1]×(number[1]+number[2]+number[1]+number[3]+number[1]+number[k])++a[k]×

(number[1]+number[k]+number[2]+number[k]++number[k1]+number[k])

=a[1]×(number[1]×(k2)+number[1]+number[2]++number[k])++a[k]×

(number[k]×(k2)+number[1]+number[2]++number[k])

至于计算number[1]+number[2]++number[k]只需要加一个前缀和优化就可以力
那么整张纸条的分数就是将所有分出来的组的分数的总和!!!

有了思路,写代码清晰无比;

时间复杂度: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 @   Hu_taooo  阅读(235)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示