题目背景

zjzjzjzjzjzjxxxxxx玩一个运气游戏,首先,在若干个卡片上各写一个正整数,然后,zjzjzjzjzjzjxxxxxx各选一张卡片,不会让对方知道,不可以相同,再把这两个数拼在一起(((zjzjzjzjzjzj选的数放在前面,例如222555拼成了252525))),如果这个数是kkk的倍数,那么zjzjzjzjzjzj赢,否则,xxxxxx赢。

可是每次玩,总是xxxxxx赢的次数多,zjzjzjzjzjzj十分不服气,就给xxxxxx出了一道题。

zjzjzjzjzjzj突发奇想,写出了这么一个式子

∑i1=1n−m+1∑i2=i1+1n−m+2......∑im=im−1+1n(ai1×ai2×......×aim)\sum\limits_{i_1=1}^{n-m+1}\sum\limits_{i_2=i_1+1}^{n-m+2}......\sum\limits_{i_m=i_{m-1}+1}^{n}(a_{i_1}\times a_{i_2}\times......\times a_{i_m})i1=1nm+1i2=i1+1nm+2......im=im1+1n(ai1×ai2×......×aim)

nnn即为卡片总数,aia_iai即为卡片上的数

题目描述

现在告诉你有nnn张卡片和模数kkkzjzjzjzjzjzj式子中的mmm,以及第iii张卡片写上了aia_iai这个数。

zjzjzjzjzjzj想知道,他一共有多少种可能会赢。

xxxxxx也想知道,zjzjzjzjzjzj出的题目的答案是多少

输入格式

共两行:

第一行两个整数n,k,mn,k,mn,k,m,表示卡片个数和模数和zjzjzjzjzjzj式子中的mmm,保证kkk不等于000

第二行nnn个整数,表示每一张卡片上写上的数aia_iai

输出格式

共两行:

第一行一个数,就是zjzjzjzjzjzj赢的可能数

第二行一个数,就是zjzjzjzjzjzj的式子的答案 mod 2147483648\bmod 2147483648mod2147483648

输入输出样例
输入 #1 复制
3 7 2
1 4 9
输出 #1 复制
3
49
输入 #2 复制
4 3 3
2 7 5 4
输出 #2 复制
8
306
输入 #3 复制
3 3 1
2 5 9
输出 #3 复制
0
16
说明/提示

对于20%20\%20%的数据:1≤n≤500,1≤m≤31\leq n\leq 500,1\leq m\leq31n500,1m3

对于100%100\%100%的数据:1≤n≤1000000,1≤m≤201\leq n\leq 1000000,1\leq m\leq201n1000000,1m20

0≤ai≤2147483647,1≤k≤1000000\leq a_i\leq2147483647,1\leq k\leq 1000000ai2147483647,1k100000

思路

对于第一问

首先,暴力是一定会TTT的,因为还要算位数和101010的幂。

那么,如果我们要看看aia_iaiaja_jaj拼起来可不可以被kkk整除,就可以用这样的式子判断

(ai×10log10(aj)+1+aj)%k=0(a_i\times10^{log_{10}(a_j)+1}+a_j)\%k=0(ai×10log10(aj)+1+aj)%k=0

aja_jaj的位数设为lenlenlen,也就是式子中的log10(aj)+1log_{10}(a_j)+1log10(aj)+1

就成了(ai×10len+aj)%k=0(a_i\times10^{len}+a_j)\%k=0(ai×10len+aj)%k=0

然后,因为两重枚举复杂度太高,那么,我们就考虑能不能两次一重的枚举,看一下当前的aia_iai可以和哪些数拼在一起被kkk整除

那么,就要在刚才的式子中分离出aia_iaiaja_jaj

(ai×10len+aj)%k=0(a_i\times10^{len}+a_j)\%k=0(ai×10len+aj)%k=0

ai×10len%k=−aj%ka_i\times10^{len}\%k=-a_j\%kai×10len%k=aj%k

为了要上面的式子成立,则取模的结果应该转换为正的。

就是加上一个kkk mod \bmodmod一个kkk

ai×10len%k=(k−aj%k)%ka_i\times10^{len}\%k=(k-a_j\%k)\%kai×10len%k=(kaj%k)%k

这样,就分离出了aia_iaiaja_jaj

但是,在一开始处理ai×10len%ka_i\times10^{len}\%kai×10len%k的时候,还要考虑到lenlenlen,因为并不清楚aja_jaj的位数,所以还要一边枚举lenlenlen,算出ai×10len%ka_i\times10^{len}\%kai×10len%k,存到一个二维数组fff中,第一维度是ai×10len%ka_i\times10^{len}\%kai×10len%k,第二维度是lenlenlen

当我们第二遍枚举aja_jaj的时候,就可以算出lenlenlen,找一下有几个aia_iai满足ai×10len%k=(k−aj%k)%ka_i\times10^{len}\%k=(k-a_j\%k)\%kai×10len%k=(kaj%k)%k,直接拿出f(k−aj%k)%k,lenf_{(k-a_j\%k)\%k,len}f(kaj%k)%k,len就是了。

不过,还要把自己和自己拼在一起的情况减掉。

最后说几句,这道题的数据会卡常数,所以,一开始先处理出来10x%k10^x\%k10x%k,枚举是直接用,这是一个很重要的优化。

时间复杂度O(10n+10n=20n)O(10n+10n=20n)O(10n+10n=20n),一开始的101010是枚举lenlenlen,后面的101010是算位数(log102147483647=10)(log_{10}2147483647=10)(log102147483647=10)

对于第二问

其实那个式子是用来唬人的。

真实的答案就是枚举mmmaia_iai,把这些aaa乘起来,然后再把所有的积加在一起就是答案。

比如说a={2,4,5,3},m=2a=\{2,4,5,3\},m=2a={2,4,5,3},m=2

那么就要算这些

A_zjzj
我们发现,再第一重枚举i1i_1i1的时候:

i1=1i_1=1i1=1时,要算2×4+2×5+2×3=2×(4+5+3)2\times4+2\times5+2\times3=2\times(4+5+3)2×4+2×5+2×3=2×(4+5+3)

i1=2i_1=2i1=2时,要算4×5+4×3=4×(5+3)4\times5+4\times3=4\times(5+3)4×5+4×3=4×(5+3)

i1=3i_1=3i1=3时,要算5×3=4×35\times3=4\times35×3=4×3

这样,不就是后缀和了吗。

O(n)O(n)O(n)一遍处理后缀和,然后O(nm−1)O(n^{m-1})O(nm1)次枚举前面的m−1m-1m1个数乘以sumim−1+1sum_{i_{m-1}+1}sumim1+1,这样,复杂度有明显的提高。

我们再开看看大一点的数据(有点丑别介意)

A_zjzj

这样,我们就要两重枚举i1,i2i_1,i_2i1,i2

i1=1i_1=1i1=1时,求得就是

1×5×sum3+1×3×sum4+1×4×sum5=1×(5×sum3+3×sum4+4×sum5)1\times5\times sum_3+1\times3\times sum_4+1\times4\times sum_5=1\times(5\times sum_3+3\times sum_4+4\times sum_5)1×5×sum3+1×3×sum4+1×4×sum5=1×(5×sum3+3×sum4+4×sum5)

i1=2i_1=2i1=2时,求得就是

5×3×sum4+5×4×sum5=5×(3×sum4+4×sum5)5\times3\times sum_4+5\times4\times sum_5=5\times(3\times sum_4+4\times sum_5)5×3×sum4+5×4×sum5=5×(3×sum4+4×sum5)

i1=3i_1=3i1=3时,求得就是

3×4×sum5=3×4×sum53\times4\times sum_5=3\times4\times sum_53×4×sum5=3×4×sum5

我们发现,每一次aia_iai都要乘上一个sumi+1sum_{i+1}sumi+1,所以,就让sumisum_isumi变成sumi+1×aisum_{i+1}\times a_isumi+1×ai(因为aia_iai要乘以sumi+1sum_{i+1}sumi+1,而为了让sumisum_isumi直接等于它们的乘积,就要这么弄),然后,刚才的东西就成了

i1=1i_1=1i1=1时,求得就是

1×sum3+1×sum4+1×sum5=1×(sum3+sum4+sum5)1\times sum_3+1\times sum_4+1\times sum_5=1\times(sum_3+ sum_4+ sum_5)1×sum3+1×sum4+1×sum5=1×(sum3+sum4+sum5)

i1=2i_1=2i1=2时,求得就是

5×sum4+5×sum5=5×(sum4+sum5)5\times sum_4+5\times sum_5=5\times(sum_4+sum_5)5×sum4+5×sum5=5×(sum4+sum5)

i1=3i_1=3i1=3时,求得就是

3×sum5=3×sum53\times sum_5=3\times sum_53×sum5=3×sum5

这不又是后缀和吗?

所以,还要再处理一次后缀和。

这样,就降了两个维度,然后,不断降降降,就会剩下一个维度,直接O(n)O(n)O(n)枚举求出答案就可以了。

时间复杂度O(nm)O(nm)O(nm)

这是真正的快

自己模拟一边想清楚就会有更深的理解

代码

#include<bits/stdc++.h>
#define maxn 1000001
#define ll long long
using namespace std;
int n,k,m;
int a[maxn],sum[maxn];
ll p[20];
int mylog(register int x){
	int t=0;
	while(x){
		x/=10;
		t++;
	}
	return t;
}
int f[100000][11];
inline void read(register int &x){
	x=0;register char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
}
int main(){
	read(n);read(k);read(m);
	register int len,i,j;
	for(i=1;i<=n;i++)
		read(a[i]);
	p[0]=1%k;
	for(i=1;i<=10;i++)
		p[i]=p[i-1]*10%k;
	for(i=1;i<=n;i++)
		for(j=1;j<=10;j++)
		     f[j][p[j]*a[i]%k]++;
	ll ans1=0;
	for(i=1;i<=n;i++){
	    len=mylog(a[i]);
		ans1+=f[len][(k-a[i]%k)%k];
		if((p[len]*a[i]%k+a[i]%k)%k==0)
			ans1--;
	}
	printf("%lld\n",ans1);
	if(m==1){
		int ans2=0;
		for(int i=1;i<=n;i++)ans2+=a[i];
		if(ans2<0)ans2+=2147483648;
		printf("%d",ans2);
		return 0;
	}
    for(i=n;i>=1;i--)
		sum[i]+=sum[i+1]+a[i];
    for(j=1;j<=m-2;j++){//因为一开始有一个后缀和,最后留一个维度,所以要重复m-2次
    	for(i=1;i<=n;i++)
			sum[i]=sum[i+1]*a[i];//乘上一个
    	for(int i=n;i>=1;i--)
			sum[i]+=sum[i+1];//后缀和
	}
	int ans2=0;
    for(i=1;i<=n-m+1;i++)
		ans2+=a[i]*sum[i+1];
    if(ans2<0)ans2+=2147483648;printf("%d",ans2);
	return 0;
}

谢谢–zhengjun

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐