【POJ】3274 Gold Balanced Lineup 哈希hash

13 篇文章 0 订阅
4 篇文章 0 订阅

对于这些英文的题目,我们的首要任务是搞清楚题目的意思。题目大意:

说实话,我起初拿到这题的时候并不认为这题和哈希有半毛钱关系……

好吧,我们还是讲题目吧。(正经脸)

1、我们把依次读入的x转化成一个二进制数c[i],得到一个01矩阵a[n][k-1]。

2、我们可以对a数组进行前缀求和,得到c数组的前缀和数组sum[n][k-1]。

3、我们枚举i和j(i>j),判断sum[i][l]-sum[j][l](0<=l<=k-1)是否等于sum[[i][0]-sum[j][0],若等于,则ans=max(ans,i-j)。注意:是i-j,而不是i-j+1,因为sum数组是前缀和。

以上都是O(n^2)的正常思路,可惜n的最大值为100000,铁定TLE。

接下来我将详细阐述正解:hash

请观察上述解法第3步的判断,若sum[i]-sum[j]=sum[i][0]-sum[j][0],则sum[i][0]-sum[j][0]=sum[i][1]-sum[j][1]=……=sum[i][k-1]-sum[j][k-1]

由上述等式可得:sum[i][1]-sum[i][0]=sum[j][1]-sum[j][0],sum[i][2]-sum[i][0]=sum[j][2]-sum[j][0]……sum[i][k-1]-sum[i][0]=sum[j][k-1]-sum[j][0]

上述等式的变形是这道题的解题关键所在。经过上述对sum数组的操作后,我们可以得到一个新的数组:c[i][j]表示sum[i][j]-sum[i][0]

然后原问题就转化成了求c数组中相同两行的最远距离,设k=hash(c[i]),在枚举h[k]中的所有元素,更新ans。

小提示:记得在更新ans之前在h[0]中加入一个元素:0,即在c数组加上第0行:全都是0的一行。

附上AC代码:

#include <cstdio>
#include <vector>
#define lyf 233333
using namespace std;

vector <int> h[lyf+1];
int n,m,x,a[100010][31],sum[100010][31],c[100010][31],ans;

int abs(int a){
	return a>0?a:-a;
}

int hash(int x){
	int ans=0;
	for (int i=0; i<=m; ++i)
		ans+=c[x][i]*i;
	return abs(ans)%lyf;
}

bool pd(int i,int j){
	for (int k=0; k<=m; ++k)
		if (c[i][k]!=c[j][k]) return 0;
	return 1; 
}

int max(int a,int b){
	return a>b?a:b;
}

int main(void){
	scanf("%d%d",&n,&m);--m;
	h[0].push_back(0);
	for (int i=1; i<=n; ++i){
		scanf("%d",&x);
		for (int j=0; j<=m; ++j){
			a[i][j]=x%2,x/=2;
			sum[i][j]=sum[i-1][j]+a[i][j];
			c[i][j]=sum[i][j]-sum[i][0];
		}
		int k=hash(i);
		for (int j=0; j<h[k].size(); ++j)
			if (pd(i,h[k][j])) ans=max(ans,abs(i-h[k][j]));
		h[k].push_back(i);
	}
	printf("%d",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值