HDU4407Sum ( 容斥原理)

bigbigship發表於2015-04-07

題目連結:

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;
}




相關文章