zoukankan      html  css  js  c++  java
  • HDU 4407 Sum [容斥+等差数列求和]

    SumSum


    Descriptionmathcal{Description}
    有一个元素为 1~n 的数列{An},有2种操作(1≤m≤1000次)
    1、求某段区间 [a,b] 中与 p 互质的数的和。
    2、将数列中某个位置元素的值改变。


    最初想法
    使用带修莫队即可.
    出于正在做容斥专题, 所以下文主要说 容斥 做法.

    对于每个 查询操作, 转化为求 [1,x][1,x]内与 pp 不互质的数的和,
    首先对 pp 分解质因数, 时间复杂度 O(N14)O(N^{frac{1}{4}}) .
    再使用 容斥 求出 [1,x][1,x] 内与 pp 不互质数的和 Tmp_1Tmp\_1,
    具体地说:
    对于容斥中的一项, xp1lfloor frac{x}{p_1} floor等差数列的项数, 设为 nn, 首项, 公差 都是 p1p_1,
    则其对答案贡献为 np1+n(n1)2p1n*p_1+frac{n*(n-1)}{2}*p_1 .
    此时再扫描前方的修改操作, 若 a>ba->b, 且在 [1,x][1,x] 区间内, O(logN)O(logN) 判断其是否与pp互质,
    减去 aa 的贡献, 加上 bb 的贡献, 存到 Tmp_2Tmp\_2 中, 时间复杂度 O(M)O(logN)O(M)*O(logN) .
    最后该询问答案即为 (1+x)x2Tmp_1+Tmp_2frac{(1+x)*x}{2}-Tmp\_1+Tmp\_2.


    正解部分
    如上.


    #include<cstdio>
    #include<cctype>
    #include<cctype>
    #include<queue>
    #define reg register
    typedef long long ll;
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 400005;
    
    int N;
    int M;
    int p_cnt;
    int op_cnt;
    int pm[maxn];
    int Op[2][maxn];
    
    ll Tmp_1;
    ll Tmp_2;
    
    bool Used[maxn];
    
    int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
    int Lcm(int a, int b){ return a/Gcd(a, b) * b; }
    
    void Div(int p){
            p_cnt = 0;
            for(reg int d = 2; d*d <= p; d ++){
                    if(p % d) continue ;
                    pm[++ p_cnt] = d;
                    while(p%d == 0) p /= d;
            }
            if(p > 1) pm[++ p_cnt] = p;
    } 
    
    ll Calc(int x){
            Tmp_1 = 0;
            for(reg int i = 1; i < 1<<p_cnt; i ++){
                    int select_num = 0, s_sum = 1;
                    for(reg int j = 1; j <= p_cnt; j ++)
                            if((1<<j-1) & i){
                                    select_num ^= 1;
                                    s_sum = Lcm(s_sum, pm[j]);
                            }
                    ll n = x/s_sum;
                    if(select_num & 1) Tmp_1 += n*s_sum + n*(n-1)/2 * 1ll*s_sum;
                    else Tmp_1 -= n*s_sum + n*(n-1)/2 * 1ll*s_sum;
            }
            //printf("Tmp_1: %lld
    ", Tmp_1);
            return (1ll+1ll*x)*x/2 - Tmp_1;
    }
    
    void Work(){
            int opt = read();
            if(opt == 2){
                    int x = read(), c = read();
                    Op[0][++ op_cnt] = x; Op[1][op_cnt] = c;
                    return ;
            }
            int x = read(), y = read(), p = read();
            if(x > y) std::swap(x, y);
            Div(p); // ~
    //        printf("L: %lld R: %lld
    ", Calc(y), Calc(x-1));
            ll Ans = Calc(y) - Calc(x-1);
    
            std::queue <int> Q; Tmp_2 = 0;
            for(reg int i = op_cnt; i >= 1; i --){
                    int a = Op[0][i], b = Op[1][i];
                    if(x <= a && a <= y && !Used[a]){
                            Used[a] = 1; Q.push(a);
                            Tmp_2 -= (Gcd(a, p)==1) * a;
                            Tmp_2 += (Gcd(b, p)==1) * b;
                    }
            }
            while(!Q.empty()){
                    int ft = Q.front(); Q.pop();
                    Used[ft] = 0;
            }
            printf("%lld
    ", Ans+Tmp_2);
    }
    
    int main(){
            freopen("a.in", "r", stdin);
            freopen("a.out", "w", stdout);
            int T = read();
            while(T --){
                    N = read(), M = read();
                    op_cnt = 0;
                    while(M --) Work();
            }
            return 0;
    }
    
    
  • 相关阅读:
    设计模式-代理模式
    设计模式-策略模式
    设计模式-单例模式
    优先队列
    n!中质因子个数
    计算组合数
    高精度
    memset用法
    质因子分解
    素数筛法
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822568.html
Copyright © 2011-2022 走看看