题解 CF1876B
题意
给定一个数组 ,一开始所有元素都为白色。你可以选定其中的至少一个元素涂成黑色,共有 种涂法。此时对于所有黑色元素 , 下标为 的倍数的所有白色元素都将变成绿色。此时数组中所有黑色和绿色元素的最大值记为此种涂法的分数。
问对于所有 种涂法,每种涂法的分数之和为多少?
题解
我们考虑涂一个黑色元素 ,记所有下标为 倍数的元素最大值为 。所有包含涂 的方法共有 种。但是这些方案中还可能涂了其他的黑色元素 ,使得 ,所以计算 的贡献中还要剔除掉这些分数比 大的方案数。
所以我们可以反着考虑,计算分数为 时的贡献。先将数组从大到小排序。 作为方案的分数当且仅当 是某个黑色元素的下标倍数,所以我们枚举 的所有因数 。如果 之前被更大的 枚举过,那么涂 的方案的分数肯定是 ,不能记到分数为 的贡献里。如果 未被枚举过,那么此时 分数为 且涂了 的方案 的贡献为 所有包含 且不包含枚举过的数的方案数 乘 (包含枚举过的数的方案,之前记过更大的分数)。
开一个标记数组标记枚举过的因数。将 数组从大到小排序。逐个考虑每一个 的所有因数,同时记录此时 至 中还未被标记的数的个数 :
-
统计未被标记的因数个数 ,并标记这些因数。
-
记录 的贡献为 ,再将 减 。
所有包含 且不包含枚举过的数的方案数 即为其中的 。这是因为此时还未标记的数还有 个,而真子集一共有 种。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100010
#define MOD 998244353
#define REP(a, b, i) for(int i = (a); i <= (b); i++)
ll dn[MAXN];
bool vis[MAXN];
int n;
struct Ai{
int ind;
ll num;
}ai[MAXN];
bool cmp(Ai x, Ai y)
{
return x.num > y.num;
}
void calc2n() // 预处理 2 的幂
{
dn[0] = 1;
REP(1, n, i)
dn[i] = ((dn[i - 1] << 1) + MOD) % MOD;
}
ll factor(ll i) //找因数
{
int cnt = 0;
for(ll k = 1; k * k <= i; k++){
if(i % k == 0){
if(!vis[k]) vis[k] = 1, cnt++;
if(!vis[i / k]) vis[i / k] = 1, cnt++;
}
}
return cnt;
}
ll cntsum(ll lf, ll ct) // 计算第 2 步中的求和
{
ll ans = 0;
for(ll i = 1; i <= ct; i++)
ans = (ans + dn[lf - i]) % MOD;
return ans;
}
void work()
{
sort(ai + 1, ai + 1 + n, cmp);
ll lft = n, res = 0; // lft 为尚未标记的数的个数
for(int i = 1; i <= n; i++){
ll k = factor(ai[i].ind);
res = (res + cntsum(lft, k) * ai[i].num % MOD) % MOD;
lft -= k;
}
cout << res;
}
int main()
{
cin >> n;
calc2n();
REP(1, n, i){
ai[i].ind = i;
cin >> ai[i].num;
}
work();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】