hdu 4407 Sum (容斥原理)

题意:

对于一个序列原来是1~n,现在又两个操作,操作1、给定区间[l,r]求这个区间中与p互质的数的个数;操作2、将区间的第i个数变成a。

题解:

如果没有替换的操作,对于求某个区间与p互质的个数,可以分别求[1,l]和[1,r]的,然后相减。对于求某个区间的数是否与p互质,明显不好求,那么我们可以求不互质的个数,不互质的个数就是p的质因子倍数的个数,这个显然要用容斥来计算,在计算时我们要把个数转化成和,dfs一下即可,计算和时用到了等差数列的前n项和。

那么没有替换操作的分析完了,那么加上替换操作该如何解决?看了下操作数,1000,恩,这个数字还算可以接受,那么我们这样做:将替换的操作记录下来,然后先计算没替换时对应的和,然后根据替换的操作进行后期处理,如果替换操作出现在当前区间,那么就单独判断下这个替换后的数,对结果进行调整。这个调整很easy的,对于操作的存储就用map就好了。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
void cmax(int& a,int b){ if(b>a)a=b; }
void cmin(int& a,int b){ if(b<a)a=b; }
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=10007;
const int maxn=400005;
const double eps=1e-8;
int p[maxn][15],f[maxn];
map<int,int>mat;

void Prime(){
    for(int i=2;i<maxn;i++){
        if(f[i])continue;
        p[i][++p[i][0]]=i;
        for(int j=i*2;j<maxn;j+=i){
            f[j]=1;
            p[j][++p[j][0]]=i;
        }
    }
}

int gcd(int a,int b){
    return b ? gcd(b,a%b) : a;
}

ll q_sum(ll d,int n){
    return d*n+d*n*(n-1)/2;
}

ll dfs(int s,ll x,int n,int num){
    ll res=0,t;
    for(int i=s;i<=p[num][0];i++){
        t=x*p[num][i];
        res+=q_sum(t,n/t)-dfs(i+1,t,n,num);
    }
    return res;
}

ll get_sum(int n,int num){
    if(n<=0)return 0;
    ll sum=(ll)n*(n+1)/2;
    return sum-dfs(1,1,n,num);
}

int main(){
    //freopen("E:\\read.txt","r",stdin);
    int T,n,m,op,a,b,num,t;
    ll ans;
    map<int,int>::iterator it;
    Prime();
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        mat.clear();
        while(m--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %d",&a,&b,&num);
                ans=get_sum(b,num)-get_sum(a-1,num);
                for(it=mat.begin();it!=mat.end();++it){
                    if(a<=it->first&&it->first<=b){
                        t=it->first;
                        if(gcd(t,num)==1)ans-=t;
                        t=it->second;
                        if(gcd(t,num)==1)ans+=t;
                    }
                }
                printf("%I64d\n",ans);
            }else{
                scanf("%d %d",&a,&num);
                mat[a]=num;
            }
        }
    }
    return 0;
}
/**
1
130 5
1 1 130 26
2 13 58
1 1 130 39
1 39 130 130
1 41 141 1
*/



你可能感兴趣的:(hdu 4407 Sum (容斥原理))