数学专题

发现自己数学太菜了,所以练练!(


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;
}

 

posted @ 2023-01-30 03:02  si_nian  阅读(70)  评论(0)    收藏  举报