hdu 6102 GCDispower

多校6 GCDispower(容斥)

题意:

给一个长度为\(n\)的排列

给q组询问 每次查询\(L,R\)内的答案
\(\sum_{i=L}^{R}\sum_{j=i+1}^{R}\sum_{k=j+1}^{R}(gcd(a[i],a[j])==a[k]) \cdot a[k]\)

题解:

考虑离线做法,固定右端点\(r\),维护每个左端点\(l\)的答案,
那么对于一个新加进来的\(A_r\), 枚举他的倍数,找出\(A_i\)满足,\(i < r\)的位置,
那么问题相当于所有\(A_i / A_r\)之后,对于任意一组\((A_i, A_j)\)满足\(i < j\) && \(gcd(A[i], A[j]) == 1\),对于左端点\(1\)\(i\)的答案数就要加\(A_r\)
因此在这些\(A_r\)的倍数这些位置,每个位置\(x\)对左端点在\([1, x]\)位置的贡献为\(x <= i < r\)这些\(Ai / Ar\)的互质个数,这个可以在从右往左扫的过程中,利用在质因子上做容斥求出,然后用树状数组维护一下答案即可。由于给的是一个全排列,每个数字枚举他的倍数均摊下来是\(n\log(n)\)
总体的时间复杂度是\(O(n\log^{2}n)\)

个人理解:

前面几步都想得到,如何对这些数做容斥才是关键
以前容斥的题都是求一个数与一段连续的区间数字互质的个数, 都是将这段区间除以这个数的每个质因子的组合,
这里的做法实在是巧妙
两个数不互质,说明存在相同的质因子组合,求一个数的不互质个数实际上是容斥计算它的所有质因子组合的个数
要更新就是更新质因子组合的个数

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ls rt<<1
#define rs (rt<<1|1)
using namespace std;
int read(){
    int x = 0;
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x;
}
const int N = 1e5 + 10;
int a[N],pos[N];
int b[N];
int vis[N];
vector<int> v[N];
vector<P> fac[N];
int value[N];
LL s[N];
LL ans[N];
int n, q;
struct query{
    int l,r,id;
    bool operator<(const query &rhs)const{
        return r < rhs.r;
    }
}qr[N];
int lowbit(int x){return x &(-x);}
void up(int pos,LL val){
    while(pos <= n){
        s[pos] += val;
        pos += lowbit(pos);
    }
}
LL getsum(int pos){
    LL ans = 0;
    while(pos){
        ans += s[pos];
        pos -= lowbit(pos);
    }
    return ans;
}
void init(){
    memset(vis,0,sizeof(vis));
    for(int i = 2;i < N;i++){
        if(!vis[i]){
            v[i].push_back(i);
            for(int j = 2 * i;j < N;j+=i) {
                v[j].push_back(i);
                vis[j] = 1;
            }
        }
    }
    for(int i = 2;i < N;i++){
        int sz = v[i].size();
        for(int s = 0;s < (1<<sz);s++){
            fac[i].push_back(P(1,1));
            for(int j = 0;j < sz;j++) {
                    if((1<<j) & s){
                            fac[i][s].first *= v[i][j];
                            fac[i][s].second *= -1;
                    }
            }
        }
    }
}
LL cal(int x){
    LL ans  = 0;
    for(int i = 0;i <fac[x].size();i++) ans += value[fac[x][i].first] * fac[x][i].second;
    return ans;
}
void up_cal(int x,int val){
    for(int i = 0;i < fac[x].size();i++) value[fac[x][i].first] += val;
}
int main(){

    init();
    int T = read();
    while(T--){
        n = read(),q = read();
        memset(pos,0,sizeof(pos));
        for(int i = 1;i<= n;i++){
            a[i] = read();
            pos[a[i]] = i;
        }
        for(int i = 0;i < q;i++){
            qr[i].l = read(),qr[i].r = read();
            qr[i].id = i;
        }
        memset(ans,0,sizeof(ans));
        memset(s,0,sizeof(s));
        sort(qr, qr + q);
        int now = 0;
        for(int i = 1;i <= n;i++){
            int cnt = 0,x = a[i];
            for(int j = 2 * x;j < N;j+=x) {
                if(pos[j] && pos[j] < i ) b[cnt++] = pos[j];
            }
            sort(b, b + cnt);
            for(int j = cnt - 1;j >= 0;j--){
                LL tmp =  cal(a[b[j]] / x);
                up(1,tmp * x);
                up(b[j]+1,-tmp * x);
                up_cal(a[b[j]] / x,1);
            }
            for(int j = cnt - 1;j >= 0;j--) up_cal(a[b[j]] / x, -1);
            while(now < q && qr[now].r <= i){
                ans[qr[now].id] =  getsum(qr[now].l);
                now++;
            }
        }
        for(int i = 0;i < q;i++) printf("%lld\n",ans[i]);
    }
    return 0;
}


posted @ 2017-08-13 19:49  jiachinzhao  阅读(282)  评论(0编辑  收藏  举报