1. 程式人生 > >hdu 4407 Sum (容斥原理)

hdu 4407 Sum (容斥原理)

題目連結:hdu 4407

 

題意:

給一個長度為n的序列,序列由1~n依次組成。 
對序列執行兩種操作: 
1.查詢[x,y]內與p互素的數的和; 
2.修改第x數為c.

 

題解:

這題我們可以先不管操作2,就按操作1去搞,因為資料很小,完全可以暴力解決操作2帶來的問題,

那麼我們可以求[1,n]內與p互素的和,最後結果就為 solve[1,y]-solve[1,x-1],再處理下操作2就行了,

求 [1,n]內與p互素的和,我們在另外一道題也做過,https://blog.csdn.net/LJD201724114126/article/details/82620754

簡單改下就行了。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>

using namespace std;
typedef long long LL;

const int maxn=400010;

int prime[maxn],cnt; ///儲存素數,素數的個數
bool book[maxn];

map<int ,int >mp;


vector<int> factor;

int gcd(int a,int b)
{
    if(!b) return a;
    else return gcd(b,a%b);
}

void Pri() ///線性篩素數
{
    memset(book,0,sizeof(book));

    cnt=0;

    book[0]=book[1]=1;

    for(int i=2;i<maxn;i++)
    {
        if(!book[i]) prime[cnt++]=i;

        for(int j=0;j<cnt&&prime[j]*i<maxn;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}

inline LL F(LL k,LL n){
    return n*(n+1)/2*k;
}

LL solve(int n) ///解決[1,n] 內與p互素的和
{
    LL sum=F(1LL,n);  ///計算前n項和

    LL item=1<<factor.size(); ///素因子個數

    LL ans=0;

    for(int i=1;i<item;i++) ///二進位制容斥,都差不多的
    {
        int num=0,x=1;

        for(int j=0;j<factor.size();j++)
        {
            if(1&(i>>j)) {
                num++;x*=factor[j];
            }
        }

        if(num&1) ans+=F(x,n/x);
        else ans-=F(x,n/x);
    }

    return sum-ans;

}
int main()
{

    int ncase;
    Pri();
    scanf("%d",&ncase);

    while(ncase--)
    {
        mp.clear(); ///注意此處,每次測試案例都要清零
        int n,m;
        scanf("%d%d",&n,&m);

        while(m--){
        int choice;
        scanf("%d",&choice);
        int x,y,c,p;
        if(choice==1){
            scanf("%d%d%d",&x,&y,&p);
            if(x>y) swap(x,y);

            factor.clear();  ///初始化

            int item=p;
            for(int i=0;i<cnt;i++) ///求出p的素因子
            {
                if(prime[i]>item) break;
                if(item%prime[i]==0){
                    factor.push_back(prime[i]);

                while(item%prime[i]==0)
                    item/=prime[i];
                }
            }

            ///這是另外一種求素因子的方法
//            for(int i=2;i*i<=item;i++){
//                if(item%i==0){
//                    factor.push_back(i);
//                    while(item%i==0) item/=i;
//                }
//            }

            if(item>1) factor.push_back(item);

            LL sum=solve(y)-solve(x-1);
//            printf("%lld %lld\n",solve(y),solve(x-1));

            map<int ,int >::iterator it;

            for(it=mp.begin();it!=mp.end();it++) ///查詢操作2對結果有沒有影響
            {
                int a=it->first,b=it->second;

                if(a>y||a<x) continue;

                if(gcd(a,p)==1) sum-=a; ///本來互素的要減掉
                if(gcd(b,p)==1) sum+=b;///修改後互素的要加上
//                printf("a=%d,b=%d\n",a,b);
            }

            printf("%lld\n",sum);

        }
        else{

            scanf("%d%d",&x,&c);
            mp[x]=c;
        }
        }
    }
    
    return 8;
}








 

 

我的標籤:做個有情懷的程式設計師。