把过去和现在的笔记记录也搬了过来,也算是给以后留个念想吧,想想一开始打acm就是图一乐,后来发现这游戏还挺上头的,也算是度过了一段电竞生涯(xs)
早些时候的笔记写的好中二,连我自己看着都羞耻。
不过,就喜欢这种羞耻的感觉。
收录的题目大部分是个人认为质量不错的题目,以DP为主,非DP的题目都用※进行了标识。
当然,有些题解思路本身也是源自其他人的,不过除非特殊标注,否则都是用的自己的代码。
题目大意:
牛客229749 LCMs
题目大意:给一个序列(1e5),数字范围1e6,求每对数的LCM之和。
解:
众所周知,lcm(a,b)=a*b/gcd(a,b),而gcd可以反演,1e6刚好支持类埃筛筛法,于是这题轻松地可以转化为对于1e6内的每个d,求得∑bc,其中b和c是d的倍数。
一旦转化成这一步,很容易就能想到能不能使用前缀和来实现,但这样的话要去记录对于每个d的倍数有哪些,空间上不支持,时间上也不支持,但是仅仅是求数对和的话,其实不一定需要记录这么多东西吧。
于是这里顺带地推出来一个简单地求数对和的方法,虽然可能仅仅适用于这题:首先记录每个数字出现的个数,然后对于每个d,记录下sum(d)和mul(d),分别是前缀和和数对和,对于每个d,遍历其倍数,每遍历到一个数字a,就在数对和里加上它造成的值,具体的算法可以手推得到为a(cnt[a]sum(d)+cnt[a](cnt[a]-1)/2),别忘了每步都要去取模。
这样的做法由于将对每个数添加到其因数到对每个因数寻找其倍数,复杂度降低到了埃筛复杂度,同时求得了f(1-n)。
之后就是利用f(1-n)反演得到g(1-n),答案就为∑g(i)/i了。
本题虽然也很简单,但是涉及到多次转换,勉强算是个能打的题目。
代码
代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <algorithm> #include <utility> #include <vector> #include <istream> #include <map> #include <cmath> #include <stack> #include <set> #include <cstring> #include <string> #include <fstream> #define ll long long #define maxn 200005 #define mdl 998244353 #define clr(a,n) for(int i=0;i<n;i++)a[i]=0 #define cfast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define pll pair<ll,ll> #define pii pair<int,int> #define inc(i,a,n) for(int i=a;i<n;i++) #define vset(a,n,m) for(int i=0;i<n;i++)a[i]=m; #define endl '\n' using namespace std; bool is_prime[1000001] = { 1,1 }; ll prime[1000001]; ll mu[1000001]; int getmu(int n) { mu[1] = 1; int cnt = 0; for (int i = 2; i <= n; i++) { if (!is_prime[i]) { prime[++cnt] = i; mu[i] = -1; } for (int j = 1; j <= cnt && i * prime[j] <= n; j++) { is_prime[i * prime[j]] = 1; if (i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } else mu[i * prime[j]] = -mu[i]; } } return cnt; } ll sum[1000005], mul[1000005]; ll cnt[1000005],ans[1000005]; ll cpow(ll x, ll n) { ll ans = 1; while (n > 0) { if (n & 1) ans = (ans * x) % mdl; x = (x * x) % mdl; n >>= 1; } return ans; } int main() { cfast;
getmu(1000000); int n; cin >> n; inc(i, 0, n) { ll a; cin >> a; cnt[a]++; } inc(i, 1, 1000001) { for (int j = i; j <= 1000000; j += i) { if (cnt[j] != 0) { mul[i] += j * (cnt[j] * sum[i] %mdl+ cnt[j] * (cnt[j] - 1) / 2%mdl*j%mdl) % mdl; } sum[i] += cnt[j] * j; sum[i] %= mdl; } } ll ss = 0; inc(i, 1, 1000001) { for (int j = i; j <= 1000000; j += i) { ans[i] += mu[j/i] * mul[j] % mdl; ans[i] %= mdl; } ans[i] *= cpow(i, mdl - 2); ans[i] %= mdl; ss += ans[i]; ss %= mdl; } cout << ss << endl; }
|
Copyright Notice: 此文章版权归EmiteInna所有,多看看吧。