HDU4407Sum ( 容斥原理)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4407


题意:

一个长度为n的序列,开始的序列为1,2,3....n;

然后又两种操作。

operation1: 询问区间[a,b]中与p互质的数的和。

operation2:将序号为i的数修改为x;

如果没有修改操作,那么狠明显就是一个容斥原理

计数的问题。

由于加上了修改,但是次数不多,因此我们可以将

修改后的点记录下来,然后先容斥记下数,然后遍历

记录的数组判断修改后的数是否要加上,之前的数

是否要去掉。


代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;

typedef long long LL;

map<int,int > mp;
map<int,int >::iterator it;

vector<int >vc;

void fen(int x){
    vc.clear();
    for(int i=2;i*i<=x;i++){
        if(x%i==0){
            vc.push_back(i);
            while(x%i==0) x/=i;
        }
    }
    if(x>1) vc.push_back(x);
}

LL solve(int x,int p){
    LL ans =(LL)x*(x+1)/2;
    fen(p);
    //cout<<"vc.size() "<<vc.size()<<endl;
    for(int i=1;i<(1<<vc.size());i++){
        int cnt = 0;
        LL tmp = 1;
        for(int j=0;j<vc.size();j++){
            if((1<<j)&i) tmp*=vc[j],cnt++;
        }
        LL k = x/tmp;
        k = (k+1)*k/2*tmp;
        if(cnt%2) ans -= k;
        else ans += k;
    }
    return ans;
}

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

int main()
{
    int t,n,m,ord,x,y,p;
    scanf("%d",&t);
    while(t--){
        mp.clear();
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            scanf("%d",&ord);
            if(ord==2){
                scanf("%d%d",&x,&y);
                mp[x]=y;
            }
            else{
                scanf("%d%d%d",&x,&y,&p);
                LL ans = solve(y,p)-solve(x-1,p);
                //printf("ans= %d\n",ans);
                for(it=mp.begin();it!=mp.end();it++){
                    if(it->first>=x&&it->first<=y){
                        if(gcd(it->first,p)==1) ans -= it->first;
                        if(gcd(it->second,p)==1) ans +=it->second;
                    }
                }
                printf("%I64d\n",ans);
            }
        }
    }
    return 0;
}




你可能感兴趣的:(HDU4407Sum ( 容斥原理))