洛谷P3941入阵曲

题面:

题意:

  给你一个的$n*m$矩阵,每个格子里都有一个不超过$k$的正整数。询问这个矩阵里有多少个不同的子矩形中的数字之是$k$的倍数?

题解:

  我们先考虑一个简化版的一维问题:给定一个长度为$n$的序列,$a[1],a[2],\cdots,a[n]$,如果某一段子序列的和为$k$的倍数,则称其为$k$倍区间,求该序列中有多少个$k$倍区间,要求时间复杂度为$O(n)$。

  设$sum$为该序列的前缀和,那么若$(sum[r]-sum[l-1])%k==0$,则区间$[l,r]$符合条件,但暴力枚举复杂度为$O(n^2)$,不符合条件。

  考虑优化,将上式变形后为$sum[l-1]%k==sum[r]%k$,所以我们可以用桶来处理在$i$之前前缀和为$sum[i]$的数量。

  继续考虑原问题,我们可以枚举两行,再枚举列,把这一列合为一个数,即可用上述方法解决。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define R register
#define ll long long
inline ll read(){
	ll aa=0;R int bb=1;char cc=getchar();
	while(cc<'0'||cc>'9')
		{if(cc=='-')bb=-1;cc=getchar();}
	while(cc>='0'&&cc<='9')
		{aa=(aa<<1)+(aa<<3)+(cc^48);cc=getchar();}
	return aa*bb;
}
const int N=403;
const int M=1e6+3;
int n,m,mod,sum[N][N],a[N];
ll ans,cnt[M];
int main()
{
	n=read();m=read();mod=read();
	for(R int i=1,num;i<=n;++i){
		num=0;
		for(R int j=1,x;j<=m;++j){
			x=read(); num+=x;
			if(num>=mod)num-=mod;
			sum[i][j]=num+sum[i-1][j];
			if(sum[i][j]>=mod)sum[i][j]-=mod;
		}
	}
	for(R int i=1;i<=n;++i){
		for(R int j=i;j<=n;++j){
			//cnt[0]=1;
			for(R int k=1;k<=m;++k){
				a[k]=(sum[j][k]-sum[i-1][k]+mod)%mod;
				ans+=cnt[a[k]];
				++cnt[a[k]];
			}
			ans+=cnt[0];
			//可理解为加上本来就为k的倍数的区间个数,可写成上述注释。
			for(R int k=1;k<=m;++k)cnt[a[k]]=0;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

 

转载于:https://www.cnblogs.com/toot-wjh/p/11336436.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷是一个在线的编程竞赛和练习平台,而P1217是洛谷上的一个题目编号。具体来说,P1217是一个关于数字问题的题目,要求编写程序找出给定范围内的所有满足特定条件的数字。 在洛谷P1217题目中,给定了一个范围 [a, b],要求找出这个范围内满足以下条件的数字: 1. 该数字的各位数字之和等于给定的一个常数d。 2. 该数字的各位数字之积等于给定的一个常数s。 你提到的是洛谷P1217java,应该是指使用Java语言来解决这个题目。你可以使用Java编写一个程序,通过遍历范围内的每个数字,判断是否满足条件,然后输出符合条件的数字。 具体的解题思路可以参考以下步骤: 1. 定义范围 [a, b] 和给定的常数d、s。 2. 使用一个循环遍历范围内的每个数字。 3. 对于每个数字,将其转换为字符串,然后遍历字符串中的每个字符,计算各位数字之和sum和各位数字之积product。 4. 判断sum是否等于d,product是否等于s,如果满足条件则输出该数字。 下面是一个简单的Java代码示例,用于解决洛谷P1217题目: ```java public class Main { public static void main(String[] args) { int a = 100; // 范围起始值 int b = 200; // 范围结束值 int d = 5; // 给定的常数d int s = 120; // 给定的常数s for (int num = a; num <= b; num++) { String strNum = String.valueOf(num); int sum = 0; int product = 1; for (int i = 0; i < strNum.length(); i++) { int digit = Character.getNumericValue(strNum.charAt(i)); sum += digit; product *= digit; } if (sum == d && product == s) { System.out.println(num); } } } } ``` 这段代码会输出在范围 [a, b] 内满足条件的数字。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值