莫比乌斯反演进阶-洛谷P2257/HDU5663
学了莫比乌斯反演之后对初阶问题没有任何问题了,除法分块也码到飞起,但是稍微变形我就跪了。用瞪眼观察法观察别人题解观察到主要内容除了柿子变形之外,主要就是对于miu函数的操作求前缀和。进而了解miu函数,miu函数是在这个数是否有平方因子的个数,每次推的套路是先用欧拉筛筛出来所有需要的函数,然后用每次需要用到的函数进行累计迭代加到前缀和,二次过筛,然后堆起来前缀和,用除法分块就行了,这个方法屡试不爽。
两道题,一道是洛谷P2257 YY的GCD
这道题求的是1-n和1-m区间内gcd为质数的个数,对此我们有暴力O(nmtlogn)算法,用莫比乌斯反演之后,我们枚举质数,每次来一遍分块,可以降到O(t * sqrtn + n),显然对于1e7这种级别的,质数都有66w个,复杂度显而易见是不够优秀的,那么我们需要一些加速,就是用前缀和处理出来这个区间内有质数gcd的数目,然后跑分块就可行了,复杂度根号
#include <bits/stdc++.h> using namespace std; #define limit (10000000 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-6 #define FASTIO ios::sync_with_stdio(false);cin.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout) #define debug(x) cout<<x<<endl typedef long long ll; typedef unsigned long long ull; inline ll read(){ ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; }//快读 void write(ll x){ if(x < 0) putchar('-'),x = -x; if(x / 10) write(x / 10); putchar(x % 10 + '0'); } int prime[limit],tot,num[limit],miu[limit]; ll sum[limit]; void get_prime(const int &n = 1e7){ memset(num,1,sizeof(num)); num[1] = num[0] = 0; miu[1] = 1; rep(i,2,n){ if(num[i])prime[++tot] = i,miu[i] = -1; for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){ num[prime[j] * i] = 0; if(i % prime[j] == 0){ miu[i * prime[j]] = 0; break; }else{ miu[i * prime[j]] = -miu[i];//莫比乌斯函数 } } } rep(i ,1,tot){ for(int j = prime[i] ; j <= n ; j += prime[i]){ sum[j] += miu[j / prime[i]];//几个平方因子 } } rep(i ,1,n){ sum[i] += sum[i-1]; } }//素数筛 ll n,m; const ll d = 1; ll calc(){ ll ans = 0; for(int l = 1,r ; l <= min(n/d,m/d); l = r + 1){ //值域分块 ll t = n / d , s = m / d; r = min(t / (t / l), s / (s / l)); ans += (sum[r] - sum[l - 1]) * (t / l) * (s / l); } return ans; } int main() { #ifdef LOCAL FOPEN; #endif get_prime(10000000); int kase = read(); while (kase--){ n = read(), m = read(); write(calc()),putchar('\n'); } return 0; }
吐槽一下卡常,这卡得什么玩意儿,快读快写都上吸了氧才ac,吐了吐了
然后是HDU 5663
这题一看就特别熟悉,直接处理出是平方数的μ前缀和,然后跑一边莫比乌斯反演就行
#include <bits/stdc++.h> using namespace std; #define limit (10000000 + 5)//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-6 #define FASTIO ios::sync_with_stdio(false);cin.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a,b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout) #define debug(x) cout<<x<<endl typedef long long ll; typedef unsigned long long ull; inline ll read(){ ll sign = 1, x = 0;char s = getchar(); while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();} while(s >= '0' && s <= '9'){x = (x << 3) + (x << 1) + s - '0';s = getchar();} return x * sign; }//快读 void write(ll x){ if(x < 0) putchar('-'),x = -x; if(x / 10) write(x / 10); putchar(x % 10 + '0'); } int prime[limit],tot,num[limit],miu[limit]; ll sum[limit]; void get_prime(const int &n = 1e7){ memset(num,1,sizeof(num)); num[1] = num[0] = 0; miu[1] = 1; rep(i,2,n){ if(num[i])prime[++tot] = i,miu[i] = -1; for(int j = 1; j <= tot && prime[j] * i <= n ; ++j){ num[prime[j] * i] = 0; if(i % prime[j] == 0){ miu[i * prime[j]] = 0; break; }else{ miu[i * prime[j]] = -miu[i];//莫比乌斯函数 } } } for(int i = 1 ; i * i <= n ; ++i){ for(int j = i * i ; j <= n ; j += i * i){ sum[j] += miu[j / i / i];//记录所有的平方 } } rep(i,2,n){ sum[i] += sum[i-1]; } }//素数筛 ll n,m; const ll d = 1; ll calc(){ ll ans = 0; for(int l = 1,r ; l <= min(n/d,m/d); l = r + 1){ //值域分块 ll t = n / d , s = m / d; r = min(t / (t / l), s / (s / l)); ans += (sum[r] - sum[l - 1]) * (t / l) * (s / l); } return ans; } int main() { #ifdef LOCAL FOPEN; #endif get_prime(1e7 + 1); int kase = read(); rep(ka,1,kase){ n = read(), m = read(); printf("%lld\n",n * m - calc()); } return 0; }
嘿哈
天才选手zerol的主页:https://zerol.me/
|
WeepingDemon的个人主页:https://weepingdemon.gitee.io/blog/