Codeforces Div.2 213 C Matrix (预处理+哈希)

超级传送门:http://codeforces.com/contest/365/problem/C


这场特别遗憾,A题题意看错WA了2次,C题溢出导致没能通过系统测试,如果C题过了就能排前30。

题意是给你一个长度不超过4000的数字s,定义b[i][j]=s[i]*s[j],则可以得到一个矩阵b;又给你一个数a,求b有多少个子矩阵其和为a。

例如:

10
12345

a=10, s=12345

生成的矩阵b为:

 1  2  3  4  5
 2  4  6  8 10
 3  6  9 12 15
 4  8 12 16 20
 5 10 15 20 25

b中和为10的子矩阵有以下:

 1  2  3  4
--------------
    4  6 
--------------
         10
--------------
    4
    6
--------------
 1
 2
 3
 4
--------------
   10

共有6个,故答案为6。

最直接的做法是枚举子矩阵,先抛开计算矩阵元素和不说,光是枚举子矩阵就是O(n^4)的,本题n的规模达到4000,所以此法肯定行不通,需要另辟蹊径,需要思考不枚举子矩阵的做法。

先构造一个数组sum,sum[i]表示s[1]+s[2]+...+s[i](设s下标从1开始),特殊地,为了便于后续计算,另sum[0]=0。

我们可以先用一个二重循环枚举子矩阵的横轴范围,枚举i和j,则当前横轴段的和为sum[j]-sum[i-1],记为val;判断a%val是否为0,如果是,说明由“多行此横轴段”构成的子矩阵满足题意;在矩阵b中,纵轴是放大倍数,我们现在只需要查看纵轴有多少段能放大a/val倍,所以需要预先哈希一下,二重循环就能搞定。

当a为0时需要特殊处理,如果某一横轴段val为0才行,此时纵轴放大倍数任意,即为n*(n+1)/2。

n最大为4000,所以最终答案可能会超int,一定要用long long。

由于s的每一位的值不超过9,所以sum[i]不会超过36000,放大倍数也不会超过36000,如果a/val>36000,那肯定是没有相应的放大倍数的(这份代码里我用了40005)。


#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

char s[4005];
int sum[4005];
int hash[40005];

int main()
{
	freopen("1.txt", "r", stdin);
	int t, n;
	while (scanf("%d", &n) > 0)
	{
		sum[0] = 0;
		memset(hash, 0, sizeof(hash));
		scanf("%s", s);
		int len = strlen(s);
		for (int i = 0; i < len; i++)
		{
			s[i] -= '0';
			if (i != 0)
			{
				sum[i + 1] = sum[i] + s[i];
			}
			else
			{
				sum[i + 1] = s[i];
			}
		}
		for (int i = 0; i <= len; i++)
		{
			for (int j = i + 1; j <= len; j++)
			{
				hash[sum[j] - sum[i]]++;
			}
		}
		long long ans = 0;
		for (int i = 0; i <= len; i++)
		{
			for (int j = i + 1; j <= len; j++)
			{
				int val = sum[j] - sum[i];
				if (n == 0 && val == 0)
				{
					ans += len * (len + 1) / 2;
				}
				else if (val != 0 && n % val == 0)
				{
					if (n / val < 40005)
					{
						ans += hash[n / val];
					}
				}
			}
		}

		printf("%I64d\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值