zjzj的游戏题解--zhengjun

题目背景

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

可是每次玩,总是 x x xx xx赢的次数多, z j z j zjzj zjzj十分不服气,就给 x x xx xx出了一道题。

z j z j zjzj zjzj突发奇想,写出了这么一个式子

∑ i 1 = 1 n − m + 1 ∑ i 2 = i 1 + 1 n − m + 2 . . . . . . ∑ i m = i m − 1 + 1 n ( a i 1 × a i 2 × . . . . . . × a i m ) \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)

n n n即为卡片总数, a i a_i ai即为卡片上的数

题目描述

现在告诉你有 n n n张卡片和模数 k k k z j z j zjzj zjzj式子中的 m m m,以及第 i i i张卡片写上了 a i a_i ai这个数。

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

x x xx xx也想知道, z j z j zjzj zjzj出的题目的答案是多少

输入格式

共两行:

第一行两个整数 n , k , m n,k,m n,k,m,表示卡片个数和模数和 z j z j zjzj zjzj式子中的 m m m,保证 k k k不等于 0 0 0

第二行 n n n个整数,表示每一张卡片上写上的数 a i a_i ai

输出格式

共两行:

第一行一个数,就是 z j z j zjzj zjzj赢的可能数

第二行一个数,就是 z j z j zjzj zjzj的式子的答案   m o d   2147483648 \bmod 2147483648 mod2147483648

输入输出样例
输入 #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 ≤ 3 1\leq n\leq 500,1\leq m\leq3 1n500,1m3

对于 100 % 100\% 100%的数据: 1 ≤ n ≤ 1000000 , 1 ≤ m ≤ 20 1\leq n\leq 1000000,1\leq m\leq20 1n1000000,1m20

0 ≤ a i ≤ 2147483647 , 1 ≤ k ≤ 100000 0\leq a_i\leq2147483647,1\leq k\leq 100000 0ai2147483647,1k100000

思路

对于第一问

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

那么,如果我们要看看 a i a_i ai a j a_j aj拼起来可不可以被 k k k整除,就可以用这样的式子判断

( a i × 1 0 l o g 10 ( a j ) + 1 + a j ) % k = 0 (a_i\times10^{log_{10}(a_j)+1}+a_j)\%k=0 (ai×10log10(aj)+1+aj)%k=0

a j a_j aj的位数设为 l e n len len,也就是式子中的 l o g 10 ( a j ) + 1 log_{10}(a_j)+1 log10(aj)+1

就成了 ( a i × 1 0 l e n + a j ) % k = 0 (a_i\times10^{len}+a_j)\%k=0 (ai×10len+aj)%k=0

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

那么,就要在刚才的式子中分离出 a i a_i ai a j a_j aj

( a i × 1 0 l e n + a j ) % k = 0 (a_i\times10^{len}+a_j)\%k=0 (ai×10len+aj)%k=0

a i × 1 0 l e n % k = − a j % k a_i\times10^{len}\%k=-a_j\%k ai×10len%k=aj%k

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

就是加上一个 k k k   m o d   \bmod mod一个 k k k

a i × 1 0 l e n % k = ( k − a j % k ) % k a_i\times10^{len}\%k=(k-a_j\%k)\%k ai×10len%k=(kaj%k)%k

这样,就分离出了 a i a_i ai a j a_j aj

但是,在一开始处理 a i × 1 0 l e n % k a_i\times10^{len}\%k ai×10len%k的时候,还要考虑到 l e n len len,因为并不清楚 a j a_j aj的位数,所以还要一边枚举 l e n len len,算出 a i × 1 0 l e n % k a_i\times10^{len}\%k ai×10len%k,存到一个二维数组 f f f中,第一维度是 a i × 1 0 l e n % k a_i\times10^{len}\%k ai×10len%k,第二维度是 l e n len len

当我们第二遍枚举 a j a_j aj的时候,就可以算出 l e n len len,找一下有几个 a i a_i ai满足 a i × 1 0 l e n % k = ( k − a j % k ) % k a_i\times10^{len}\%k=(k-a_j\%k)\%k ai×10len%k=(kaj%k)%k,直接拿出 f ( k − a j % k ) % k , l e n f_{(k-a_j\%k)\%k,len} f(kaj%k)%k,len就是了。

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

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

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

对于第二问

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

真实的答案就是枚举 m m m a i a_i ai,把这些 a a a乘起来,然后再把所有的积加在一起就是答案。

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

那么就要算这些

A_zjzj
我们发现,再第一重枚举 i 1 i_1 i1的时候:

i 1 = 1 i_1=1 i1=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)

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

i 1 = 3 i_1=3 i1=3时,要算 5 × 3 = 4 × 3 5\times3=4\times3 5×3=4×3

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

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

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

A_zjzj

这样,我们就要两重枚举 i 1 , i 2 i_1,i_2 i1,i2

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

1 × 5 × s u m 3 + 1 × 3 × s u m 4 + 1 × 4 × s u m 5 = 1 × ( 5 × s u m 3 + 3 × s u m 4 + 4 × s u m 5 ) 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)

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

5 × 3 × s u m 4 + 5 × 4 × s u m 5 = 5 × ( 3 × s u m 4 + 4 × s u m 5 ) 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)

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

3 × 4 × s u m 5 = 3 × 4 × s u m 5 3\times4\times sum_5=3\times4\times sum_5 3×4×sum5=3×4×sum5

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

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

1 × s u m 3 + 1 × s u m 4 + 1 × s u m 5 = 1 × ( s u m 3 + s u m 4 + s u m 5 ) 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)

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

5 × s u m 4 + 5 × s u m 5 = 5 × ( s u m 4 + s u m 5 ) 5\times sum_4+5\times sum_5=5\times(sum_4+sum_5) 5×sum4+5×sum5=5×(sum4+sum5)

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

3 × s u m 5 = 3 × s u m 5 3\times sum_5=3\times sum_5 3×sum5=3×sum5

这不又是后缀和吗?

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

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

时间复杂度 O ( n m ) 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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Windows下的g编译器主要是指GNU编译器套件(GNU Compiler Collection),它是一个类Unix的操作系统,由GNU计划推动。GNU编译器套件包含了多个版本,其中有一些专门用于Windows操作系统。比如win32-sjlj-rt_v6-rev0六个版本中的i686是用于32位Windows操作系统的,x86_64则是用于64位Windows操作系统的。此外,GNU编译器套件还提供了多个版本的C语言编译器,如c/gcc98、c/gcc11、c/gcc14、c/gcc17等。如果要使用C17编译,请注意如果是64位系统,则需要选择对应的版本。请注意,在Windows下使用gcc时,生成的可执行文件扩展名会变成.exe,而不是.out。所以,如果在Windows下使用g编译器进行编译,生成的可执行文件将会是main.exe。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [MinGW64 32/64位 Windows 完整版](https://download.csdn.net/download/A_zjzj/85137139)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [windows 下使用g++ 编译器](https://blog.csdn.net/piaoliangjinjin/article/details/82457750)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A_zjzj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值