牛客 周赛113 20251017
牛客 周赛113 20251017
https://ac.nowcoder.com/acm/contest/119225
A:
题目大意:给定正整数计算所有数位之和对 \(9\) 取模的结果
void solve(){
string s;
cin >> s;
int sum = 0;
for (auto c : s)
sum += c - '0';
cout << sum % 9;
}
签到
B:
题目大意:给定 \(n\) 个负数 \(a+b\times i\) 的 \(a_i,b_i\) ,计算这 \(n\) 个负数的乘积
const int mod = 1e9 + 7;
void solve(){
int n;
cin >> n;
vector<pair<LL, LL>> vt(n);
for (auto &x : vt) cin >> x.first >> x.second;
pair<LL, LL> ans = vt.front();
auto cal = [&](pair<LL, LL> a, pair<LL, LL> b){
LL x = a.first * b.first - a.second * b.second;
LL y = a.first * b.second + a.second * b.first;
x %= mod;
y %= mod;
if (x < 0) x += mod;
if (y < 0) y += mod;
return make_pair(x, y);
};
for (int i = 1; i < n; i ++)
ans = cal(ans, vt[i]);
cout << ans.first << ' ' << ans.second;
}
模拟负数计算的方式即可,时间复杂度 \(O(n)\)
C:
题目大意:给定长为 \(n\) 的数组,数组所有元素乘积为 \(x\) ,计算最大的满足 \(x\%30^k=0\) 的 \(k\) 为多少
void solve(){
int n;
cin >> n;
vector<LL> a(n);
for (auto &x : a) cin >> x;
map<int, int> mp;
int ans = 0;
for (auto x : a){
while (x % 2 == 0){
mp[2] ++;
x /= 2;
}
while (x % 3 == 0){
mp[3] ++;
x /= 3;
}
while (x % 5 == 0){
mp[5] ++;
x /= 5;
}
}
cout << min(mp[2], min(mp[3], mp[5]));
}
对 \(30\) 进行质因数分解得到 \(30=2\times3\times 5\) ,那么合适的 \(k\) 和 \(x\) 满足关系:
对数组所有元素分解后计算 \(2,3,5\) 这三个因子的数量,最终答案为 \(\min(cnt_2,cnt_3,cnt_5)\)
D:
题目大意:
void solve(){
int n;
cin >> n;
vector<LL> p(n + 1, 1);
for (int i = 1; i <= n; i ++) p[i] = p[i - 1] * i % mod;
LL ans = 0;
for (int i = 1; i <= n; i ++){
ans += p[n - 1] * (i % 5);
ans %= mod;
}
cout << ans;
}
一个元素对 \(5\) 取模的结果是这个数个位对 \(5\) 取模的结果
简单证明:设 \(x\) 是一个大于 \(10\) 的整数,那么 \(x\) 可以被表示为 \(10\alpha +\beta\) ,显然 \(10\alpha\) 一定为 \(5\) 的倍数,所以 \((10\alpha+\beta)\%5=\beta\%5\)
考虑一个元素在个位产生的贡献,其余 \(n-1\) 个元素有 \((n-1)!\) 种方案
E:
题目大意:
LL dpa[2][310][N], dpb[2][310][N];
LL pb[310][N];
void solve(){
int n;
cin >> n;
vector<int> a(n + 1), b (n + 1);
for (int i = 1; i <= n; i ++) cin >> a[i], a[i] %= N;
for (int i = 1; i <= n; i ++) cin >> b[i], b[i] %= N;
dpa[0][0][0] = 1;
for (int i = 1; i <= n; i ++){
for (int j = 0; j <= i; j ++)
for (int k = 0; k < N; k ++)
dpa[i % 2][j][k] = 0;
for (int j = 0; j <= i; j ++){
for (int k = 0; k < N; k ++){
if (j > 0)
dpa[i % 2][j][k] += dpa[!(i % 2)][j - 1][(k - a[i] + N) % N];
dpa[i % 2][j][k] += dpa[!(i % 2)][j][k];
dpa[i % 2][j][k] %= mod;
}
}
}
dpb[0][0][0] = 1;
for (int i = 1; i <= n; i ++){
for (int j = 0; j <= i; j ++)
for (int k = 0; k < N; k ++)
dpb[i % 2][j][k] = 0;
for (int j = 0; j <= i; j ++){
for (int k = 0; k < N; k ++){
if (j > 0)
dpb[i % 2][j][k] += dpb[!(i % 2)][j - 1][(k - b[i] + N) % N];
dpb[i % 2][j][k] += dpb[!(i % 2)][j][k];
dpb[i % 2][j][k] %= mod;
}
}
}
for (int i = 0; i < N; i ++){
for (int j = 0; j <= n; j ++){
pb[j][i] += dpb[n % 2][j][i];
if (j > 0) pb[j][i] += pb[j - 1][i];
pb[j][i] %= mod;
}
}
for (int i = 0; i < N; i ++){
LL ans = 0;
for (int j = 0; j < N; j ++){
for (int k = 0; k <= n; k ++){
ans += dpa[n % 2][k][j] * pb[k][(i - j + N) % N] % mod;
ans %= mod;
}
}
cout << ans << ' ';
}
}
因为要考虑 \(b\) 中选取的元素个数小于等于 \(a\) 中选取的元素个数
所以定义 \(dp_{i,j,k}\) 表示在数组前 \(i\) 个元素中选取 \(j\) 个,他们的和为 \(k\) 的方案数
状态转移为:
然后枚举计算答案
for (int i = 0; i < N; i ++){
LL ans = 0;
for (int j = 0; j < N; j ++){
for (int k = 0; k <= n; k ++){
ans += dpa[n % 2][k][j] * pb[k][(i - j + N) % N] % mod;
ans %= mod;
}
}
cout << ans << ' ';
}
元素和为 \(i\) 的方案是从 \(a\) 中选取 \(j\) 个元素且元素和为 \(k\) 的方案数,乘上从 \(b\) 中选取 \([0,j]\) 个元素且元素和为 \(495-k\) 的方案数
预处理出所有的 \(dpb\) 在第二维上,有关所有的元素和的前缀和,统计答案的时间复杂度为 \(O(495^2n)\)
F:
题目大意:
void solve(){
int n, q;
cin >> n >> q;
vector<LL> a(n + 1);
for (int i = 1; i <= n; i ++) cin >> a[i];
vector<vector<LL>> tr(13,vector<LL> (n + 1));
auto update = [&](int st, int x, int k){
while (x <= n){
tr[st][x] += k;
x += x & -x;
}
};
auto query = [&](int st, int x){
int res = 0;
while (x){
res += tr[st][x];
x -= x & -x;
}
return res;
};
const int M[] = {0, 100, 200, 110, 210, 101, 201, 111, 211, 10, 1, 11};
auto endo = [&](int x){
int d[3] = {0};
while (x % 3 == 0){
d[0] ++;
x /= 3;
}
while (x % 5 == 0){
d[1] ++;
x /= 5;
}
while (x % 11 == 0){
d[2] ++;
x /= 11;
}
d[0] = min(d[0], 2LL);
d[1] = min(d[1], 1LL);
d[2] = min(d[2], 1LL);
int sum = d[0] * 100 + d[1] * 10 + d[2];
for (int i = 0; i < 12; i ++) if (sum == M[i]) return i;
return 0LL;
};
auto check = [&](int x, int y){
int a[3] = {x / 100, x % 100 / 10, x % 10};
int b[3] = {y / 100, y % 100 / 10, y % 10};
return a[0] + b[0] >= 2 && a[1] + b[1] >= 1 && a[2] + b[2] >= 1;
};
for (int i = 1; i <= n; i ++)
update(endo(a[i]), i, 1);
while (q --){
int op, x, y;
cin >> op >> x >> y;
if (op == 1){
update(endo(a[x]), x, -1);
a[x] = y;
update(endo(a[x]), x, 1);
}
else{
int cnt[12];
LL ans = 0;
for (int i = 0; i < 12; i ++)
cnt[i] = query(i, y) - query(i, x - 1);
for (int i = 0; i < 12; i ++){
if (check(M[i], M[i])) ans += cnt[i] * (cnt[i] - 1) / 2;
for (int j = i + 1; j < 12; j ++)
if (check(M[i], M[j])) ans += cnt[i] * cnt[j];
}
cout << ans << '\n';
}
}
}
对 \(495\) 进行质因数分解有 \(495=3^2\times 5\times 11\)
对于一个询问,选出的两个数只要满足他们的质因子的数量满足 \(cnt_3\ge 2,cnt_5\ge1.cnt_{11}\ge1\) 那么这就是一种方案
不难想到利用树状数组进行 \(O(\log n)\) 的修改和查询操作
我们将数组的每个元素根据 \(3,5,11\) 这三个质因子的数量划分为 \(12\) 类
\(cnt_3\in\{0,1,\ge2\},cnt_5\in\{0,\ge1\},cnt_{11}\in\{0,\ge1\}\)
const int M[] = {0, 100, 200, 110, 210, 101, 201, 111, 211, 10, 1, 11};//对这里类别进行编号
auto endo = [&](int x){//对x进行划分类别操作
int d[3] = {0};
while (x % 3 == 0){
d[0] ++;
x /= 3;
}
while (x % 5 == 0){
d[1] ++;
x /= 5;
}
while (x % 11 == 0){
d[2] ++;
x /= 11;
}
d[0] = min(d[0], 2LL);
d[1] = min(d[1], 1LL);
d[2] = min(d[2], 1LL);
int sum = d[0] * 100 + d[1] * 10 + d[2];//用百位记录3的个数,十位记录5的个数,个位记录11的个数
for (int i = 0; i < 12; i ++) if (sum == M[i]) return i;
return 0LL;
};
然后构造 \(12\) 个树状数组记录区间的信息
for (int i = 1; i <= n; i ++)
update(endo(a[i]), i, 1);
对于询问的处理,判断哪两种类别的乘积可以为 \(495\) 的倍数
auto check = [&](int x, int y){
int a[3] = {x / 100, x % 100 / 10, x % 10};
int b[3] = {y / 100, y % 100 / 10, y % 10};
return a[0] + b[0] >= 2 && a[1] + b[1] >= 1 && a[2] + b[2] >= 1;
};
就是对这两个类别计算他们是否满足 \(cnt_3\ge 2,cnt_5\ge1.cnt_{11}\ge1\)
for (int i = 0; i < 12; i ++){//枚举第一个元素的类别
for (int j = i + 1; j < 12; j ++)//枚举第二个元素的类别,从 i+1 开始
if (check(M[i], M[j])) ans += cnt[i] * cnt[j];
}
如果一种类别自己和自己也可以满足要求,那么这样的方案数需要单独计算
if (check(M[i], M[i])) ans += cnt[i] * (cnt[i] - 1) / 2;