HDU - 4407 Sum (容斥)

题意:初始序列[1..N](1<=N<=4e5),支持两种操作:1.求区间[x,y]内与p互素的数之和; 2。将x位置的数变为c。

分析:很容易把人骗到线段树的思维中,而实际上操作2单点的修改可以用map去记录,之后统计和的时候再去检查是否有给定区间内的数被修改。

区间[x,y]内与p互素的数之和,可以转化成求与p不互素的数之和。设p的质因子有[f1,f2...fk],则若干个质因子积的倍数一定不与p互素,用容斥求出在[x,y]区间内与p的质因子积的倍数。根据等差数列求和算出[x,y]区间的和,并检查区间内被修改的值,若原值与p互素,则要减去;若修改后的值与p互素,则要加上。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e5+5;
typedef long long LL;
map<int,int> vz;

LL gcd(LL a,LL b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}

LL sum1(LL a1,LL an)         //等差数列求和
{
    LL n  = an-a1+1;
    LL res = (a1 + an) * n / 2;
    return res;    
}

LL sum2(LL l,LL r,LL val)     //等比数列求和
{
    int n = ( r / val ) - ( ( l - 1 ) / val ) ;
    int a1 = ( l % val == 0 )? l : ( val - l % val ) + l ;
    int an = r - r % val ;
    LL res = (LL)( a1 + an ) * (LL)n / 2 ; 
    return res;
}

LL cal(int l,int r,int p)
{
    vector<int> fac;
    int tmp = p;
    for(int i=2;i*i<=tmp;++i){
        if(tmp%i==0){
            fac.push_back(i);
            while(tmp%i==0) tmp/=i;
        }
    }
    if(tmp>1) fac.push_back(tmp);
    int cnt = fac.size();
    int up = 1<<cnt;
    LL res=0;
    for(int i=1;i<up;++i){                          //容斥统计与p不互素的数的和
        int bits = 0 ;
        LL ji = 1;
        for(int j=0;j<cnt;++j){
            if(i&(1<<j)){
                bits++;
                ji *= fac[j];
            }
        }
        LL sum= sum2(l,r,ji);
        if(bits &1) res+=sum;
        else res-=sum;         
    }
    res = sum1(l,r)-res;
    for(auto &v :vz){
        if(v.first<l) continue;
        if(v.first>r) break;
        if(gcd(v.first,p)==1) res-=v.first;         //多加了要减去
        if(gcd(v.second,p)==1) res+=v.second;       //修改后的结果与p互质,加上
    }
    return res; 
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int T; scanf("%d",&T);
    while(T--){
        int N,M; scanf("%d%d",&N,&M);
        vz.clear();
        int op,x,y,p,c;
        while(M--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d%d",&x,&y,&p);
                if(x>y) swap(x,y);
                printf("%lld\n",cal(x,y,p));
            }
            else{
                scanf("%d%d",&x,&c);
                vz[x] =c;                   //记录修改
            }
        }
    }
    return 0;
}

 

posted @ 2018-08-21 10:41  xiuwenL  阅读(107)  评论(0编辑  收藏  举报