#P3394. 第1题-满减活动
-
1000ms
Tried: 178
Accepted: 56
Difficulty: 4
所属公司 :
拼多多
时间 :2025年8月17日
-
算法标签>数学
第1题-满减活动
解题思路
余数分组
- 求余数:把每个价格取模 m,记余数为 r=aimodm。
- 统计频次:用数组
cnt[r]记录余数 r 出现的次数,大小为 m。
配对规则
-
若两件商品余数分别为 r 与 m−r(1≤r <m),则 r+(m−r)≡0(modm)。
-
特殊余数:
- r=0 :只能与自身配对,组合数为 (2cnt[0])
- 当 m 为偶数且 r=m/2 时,同上,组合数为 (2cnt[m/2])
-
其它余数 1≤r<2m:可与 m−r 配对,组合数为
cnt[r] * cnt[m-r]。
组合数公式
(2k)=2k(k−1)总体算法
- 统计余数出现次数 O(n)
- 遍历 r = 1 … ⌊(m-1)/2⌋ 计算配对数
- 处理 r = 0 和 (m/2)(若 m 为偶数)
- 全程对 MOD 取模
复杂度分析
- 时间:O(n+m)(统计 + 遍历余数)
- 空间:O(m)(存余数计数)
代码
Python
import sys
MOD = 998244353
n, m = map(int, sys.stdin.readline().split())
a = list(map(int, sys.stdin.readline().split()))
cnt = [0] * m # 统计每个余数出现次数
for v in a:
cnt[v % m] += 1
ans = cnt[0] * (cnt[0] - 1) // 2 # r = 0
for r in range(1, (m + 1) // 2): # 1 <= r < m/2
ans += cnt[r] * cnt[m - r]
if m % 2 == 0: # r = m/2
ans += cnt[m // 2] * (cnt[m // 2] - 1) // 2
print(ans % MOD)
Java
import java.io.*;
import java.util.*;
public class Main {
static final long MOD = 998244353L;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
long[] cnt = new long[m]; // 余数计数
st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
long v = Long.parseLong(st.nextToken());
cnt[(int) (v % m)]++;
}
long ans = cnt[0] * (cnt[0] - 1) / 2; // r = 0
for (int r = 1; r < (m + 1) / 2; r++) { // 1 <= r < m/2
ans = (ans + cnt[r] * cnt[m - r]) % MOD;
}
if (m % 2 == 0) { // r = m/2
long c = cnt[m / 2];
ans = (ans + c * (c - 1) / 2) % MOD;
}
System.out.println(ans % MOD);
}
}
C++
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 998244353LL;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
if (!(cin >> n >> m)) return 0;
vector<long long> cnt(m, 0); // 余数计数
for (int i = 0; i < n; ++i) {
long long x; cin >> x;
++cnt[x % m];
}
long long ans = cnt[0] * (cnt[0] - 1) / 2; // r = 0
for (int r = 1; r < (m + 1) / 2; ++r) { // 1 <= r < m/2
ans += cnt[r] * cnt[m - r];
}
if (m % 2 == 0) { // r = m/2
ans += cnt[m / 2] * (cnt[m / 2] - 1) / 2;
}
cout << ans % MOD << '\n';
return 0;
}
题目内容
多多正在参加一个特殊的满减活动,有几个名不相同的商品,每个商品的价格是ai。活动规则是——若挑选的两个商品的价格总和是m的倍数的话,可以免费带走这两个商品,由于多多最多只能带两个商品,请问多多有多少种组合方式免费带两个商品。
输入描述
第一行输入两个数字n和m。
接下来输入n个商品的价格ai,ai为整数。
1≤n≤200000,1≤m<200000,1≤ai≤1000000000
输出描述
输出一个数字,表示多多鸡能免费带走两个商品的方式数量。
最终结果对998244353取模
样例1
输入
2 4
1 3
输出
1
说明
多多只有1种方式免费拿走两件商品。
第1件商品和第2件商品
样例2
输入
5 2
1 2 3 4 5
输出
4
说明
多多有4种方式带走两件商品
第1件商品和第3件商品
第2件商品和第4件商品
第1件商品和第5件商品
第3件商品和第5件商品