数学专题
发现自己数学太菜了,所以练练!(
CF1780E Josuke and Complete Graph
链接:https://codeforces.com/contest/1780/problem/E
考虑$gcd$本质上是个啥。
假设我们现在知道某个数$d$,那么两个数$a$和$b$的$gcd$是$d$的条件其实是:
$a = k_1 d$,$b =k_2 d$且$(k_1,k_2) = 1$
又因为$(x,x+1) = 1$,所以其实$k_1$和$k_2$最接近的时候是差$1$
那么,我们现在假设我们枚举出某个数$d$,考虑什么时候$d$才能作为最大公约数出现在$[L,R]$之间
设第一个出现在$[L,R]$之间的$d$的倍数是$k_1 d$,则只要$(k_1 + 1) d$也在$[L,R]$内,$d$就能作为答案。
$k_1 = [\frac{L}{d}]$,$[ ]$表示上取整
有$(k_1 + 1) d <= R$
$d<=\frac{R}{k_1 + 1}$
显然,$k_1$是可以整除分块的。分块的过程中,$ [\frac{L}{d}] = k_1$时,范围为$[l,r]$
把两个$d$的范围取个交集,就是我们可以成为答案的$d$
在上整除分块中,$1$要单独处理一下。
#include <bits/stdc++.h> using namespace std; int main(){ int T; cin>>T; while (T--){ long long L,R; cin>>L>>R; long long ans = 0; ans = max(0ll,R/2-L+1); for (long long l=1,r;l<=L;l=r+1){ long long t = ceil(double(L)/double(l)); if (t == 1) break; r = (L-1)/(t-1); ans += max(0ll,min(r,R/(t+1)) - l + 1); } printf("%lld\n",ans); } return 0; }
CF1780F Three Chairs
链接:https://codeforces.com/contest/1780/problem/F
发现正面算这个互素很麻烦
不妨反过来考虑,进行一手容斥。
因为当前的数字范围是$3*10^5$,所以其实最多也就$2*3*5*7*11*13*17$,$7$个不同的质因子。
大可以放心的$2^n$容斥,枚举因数。
接下来就考虑怎么容斥。
首先,显然要先把数组从小到大排个序。
然后考虑,如何计算当前含有$d$这个因数的数的答案。
其实如果把有$d$的部分看作$1$,没有的部分看作$0$,那么其实就是每次新加入$1$时,在前面的$1$里随便取一个,然后再在它们中间随便挑一个数即可。
所以答案是$\sum pos - pos_j-1 = n*pos - \sum pos_j - n$
于是开个数组每次$O(1)$维护即可。
复杂度是$O(2^k * n)$,$k$为不大于$7$的常数
分解质因数如果直接$\sqrt{n}$枚举的话会$T$,线筛出每个数的$minp$即可。
#include <bits/stdc++.h> using namespace std; #define ll long long int N; int a[300005]; int mp[300005],Prime[300005],Index; bool IsPrime[300005]; ll Smp[300005],Cmp[300005]; vector<int> mp1; void Pre(){ for (int i = 2 ; i <= 300000 ; i ++){ if (!IsPrime[i]){ Prime[++Index] = i; mp[i] = i; } for (int j = 1 ;i*Prime[j]<=300000 && j<=Index ; j++){ IsPrime[i*Prime[j]] = 1; mp[i*Prime[j]] = Prime[j]; if (i%Prime[j] == 0) break; } } } long long Get(int x,int pos){ mp1.clear(); while (x!=1){ int y = mp[x]; mp1.push_back(mp[x]); while (x%y==0){ x/=y; } } int sz = mp1.size(); int ST = (1<<sz)-1; ll Ans = 0; for (int i = 0 ; i <= ST ; i ++){ int D = 1; int cnt = 0; for (int j = 0 ; j < sz ; j ++) if (i&(1<<j)) {D *= mp1[j]; cnt++;} if (cnt & 1) Ans -= (Cmp[D]*pos-Smp[D]-Cmp[D]); else Ans += Cmp[D]*pos-Smp[D]-Cmp[D]; } return Ans; } void Insert(int x,int pos){ mp1.clear(); int x1 = x; while (x!=1){ int y = mp[x]; mp1.push_back(mp[x]); while (x%y==0){ x/=y; } } int sz = mp1.size(); int ST = (1<<sz)-1; int Ans = 0; for (int i = 0 ; i <= ST ; i ++){ int D = 1; int cnt = 0; for (int j = 0 ; j < sz ; j ++) if (i&(1<<j)) {D *= mp1[j]; cnt++;} Smp[D] += pos; Cmp[D] ++; } } int main(){ scanf("%d",&N); for (int i = 1 ; i <= N ; i ++) scanf("%d",&a[i]); sort(a+1,a+N+1); Pre(); long long ans = 0; for (int i = 1 ; i <= N ; i ++){ ans += Get(a[i],i); Insert(a[i],i); } cout<<ans; return 0; }

浙公网安备 33010602011771号