To_Heart—题解——Hash 键值

今天,带大家看一看一道思维题。。。

Hash 键值 (hash)

题目描述

Marser沉迷hash无法自拔,然而他发现自己记不住hash键值了……

Marser使用的hash函数是一个单纯的取模运算,每一个数i被对应到i mod p。他现在有一个数列,他采用这种方法,把每一个数对应到一个键值i mod p。他想知道对于给定的模数p和键值r,所有对应到该键值的数的和为多少。同时,Marser可能会发现他的数列出了一些问题,所以他还想随时更改数列中任意一项的值。

现在Marser有q个请求,每个请求可能是修改或是询问。对于每一个询问,你需要给出正确的答案。如果你不能在1s内正确回答所有询问,Marser就会让hotwords把你给续了。

输入格式

从文件hash.in读入。

第一行两个整数n,q,表示数列长度和请求数量。

第二行n个整数,表示初始的序列。

接下来m行,每行三个整数opt,x,y;

若opt=1,则询问在mod x时,所有对应到键值y的数的和。

若opt=2,则将数列第x项修改为y。

输出格式

输出到文件hash.out中。

样例

样例输入 1

10 5
1 2 3 4 5 6 7 8 9 10
1 2 1
2 1 20
1 3 1
2 5 1
1 5 0

样例输出 1

25
41
11

首先,我们发现这是一道水题

轻轻松松写下下面的代码

#include <bits/stdc++.h>
using namespace std;
int a[100005];

int main() {
    int n, m;
    freopen("hash.in","r",stdin);
    freopen("hash.out","w",stdout);//文件输入输出
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= m; ++i) {
        int x;
        scanf("%d", &x);
        if (x == 1) {
            int x1, x2, ans = 0;
            scanf("%d%d", &x1, &x2);
            for (int i = x2; i <= n; i += x1) {  //不需要一个一个枚举,只需要算x1的倍数就行了;
                ans += a[i]; 
            }
            printf("%d\n", ans);
        }
        if (x == 2) {
            int x1, x2;
            scanf("%d%d", &x1, &x2);
            a[x1] = x2;  //维护
        }
    }
}

然后我们发现:

轻轻松松超时

竟然可以拿30分!!!

咳咳,言归正传,我们开始讨论100分的代码

开始吟唱

Frist:

我们需要算一下时间复杂度

原代码时间复杂度为O(n+mn/x1);

所以我们的代码一定要小于这个时间复杂度;

second

我们发现,无论如何原代码的时间复杂度是减少不了的了至少我没想出啦。。。,所以只能:

另辟蹊径:

仔细一想,不难得出:如果我们能在x1,x2读入的时候,就直接输出ans,那么时间复杂度肯定会大大降低;

那么我们怎么才可以实现这种操作呢?

首先,定义一个名为sum的二维数组,sum[i][j]表示所有模i余j的数的总和;

再输入一个a[i]以后,就用一重循环遍历,代码如下:

for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		for(int j=1;j<n;++j){
			sum[j][i%j]+=a[i];  //除j模(i%j);
		}
	}

这样,我们其实就可以达成输入就输出啦!!!

但我们再来算算时间复杂度为O ( n 2 ) (n^2) (n2)

然后我们神奇的发现,时间复杂度竟然比原来还大!

那么,如果我们不遍历所有值,而是只遍历log(n),再把x1>log(n)的情况用第一种方法运算呢???

再算算时间复杂度:O= ( n log ⁡ 2 m + max ⁡ ( m n / x 1 , m n ) ) (n\log_2^m+\max(mn/x1,mn)) (nlog2m+max(mn/x1,mn));

化简一下:O= ( n log ⁡ 2 m + m n x 1 ) (n\log_2^m+\frac {mn}{x1}) (nlog2m+x1mn);

然后我们就可以过剩下的测试点啦!!!
上代码!!!:

#include<bits/stdc++.h>
using namespace std;
int a[1000005],sum[1005][1005]; //sum[i][j]等于所有除以i余j的数的和; 

int main(){
	freopen("hash.in","r",stdin);
    freopen("hash.out","w",stdout);  //文件输入输出 
	int n,m;
	scanf("%d%d",&n,&m);
	int num=sqrt(n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		for(int j=1;j<num;j++){
			sum[j][i%j]+=a[i];   //对sum进行赋值,循环j模拟i除以的数之和; 
		}
	}
	for(int i=1;i<=m;++i){
		int x;
		scanf("%d",&x);
		if(x==1){
			int x1,x2,ans=0;
			scanf("%d%d",&x1,&x2);
			if(x1<num){//如果x1<num,说明x1在sum处理的范围内; 
				printf("%d\n",sum[x1][x2]);  //直接通过前文对sum的处理输出; 
			}
			else{							
				for(int i=x2;i<=n;i+=x1){			//方法一; 
					ans+=a[i]; 
				}
				printf("%d\n",ans);	
			}
		}
		if(x==2){
			int x1,x2;
			scanf("%d%d",&x1,&x2);
			for(int i=1;i<=num;i++)
                sum[i][x1%i]=sum[i][x1%i]-a[x1]+x2;  //维护sum数组,把a[x1]覆盖成x2,并确保sum里的值正确; 
            a[x1]=x2;  //对a[i]也要进行维护; 
		}
	}
}
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值