hdu 4407,4059,3501 容斥原理

hdu 3501 传送门:点击打开链接

题意:求小与整数n不互质的数的sum。

思路:这题有2种解法 

先说第一种;用容斥原理。与n不互质 ,即gcd(x,n)!=1,即  符合这样的数 都是n的素因子的倍数,所以我们先求出n的素因子,然后用容斥来求就可以了。这里我们对于 p(素数)的倍数的和 怎么求进行一下说明。我们知道  小于n并且是p的倍数 有  p,2*p,3*p,4*p.......[(n-1)/p]*p(注:这里中括号 代表向下取整),我们可以把p因子提出来那么 p*(1+2+3+4+5......+[(n-1)/p])   这就是求p的倍数 的和,而且我们发现 后面的式子正好可以用求和公式来求   那么就等于  (我们设[(n-1)/p]=q)  sum=p*q*(q+1)/2,为什么用容斥呢? 例如  n=12,那么 他的素因子 有2,3,当我们求2的倍数sum时 算了3*2=6一次  ,而我们算3的倍数sum 也有2*3=6,如果在加那么就是2次了,所以要用到容斥。。

代码:

#include <iostream>
#include <cmath>
#include <cstring>
#define  rep(i,o,u) for(int i=(int)(o);i<=(int)(u);++i)
#define  clr(a,x)  memset(a,(x),sizeof(a))
using namespace std;
typedef long long  ll;
const ll mod=1000000007;
const int maxn=1e5;
int n;int cnt=0;int inde=0;
ll data[100];
bool mark[maxn];
int prime[maxn];
void Prime()
{
    clr(mark,0);
    rep(i,2,maxn)
    {
        if(!mark[i])
            prime[inde++]=i;
        rep(j,0,inde-1)
        {
            if(i*prime[j]>=1e5-2)
                break;
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
}
void solve()
{
    cnt=0;
    int t=n;
    rep(i,0,inde-1)
    {
        if(prime[i]*prime[i]>t)
            break;
        if(t%prime[i]==0)
        {
            data[cnt++]=prime[i];
            while(t%prime[i]==0)
                t/=prime[i];
        }
    }
    if(t!=1)
        data[cnt++]=t;
    int e=1<<cnt;ll ans=0;
    rep(i,1,e-1)
    {
        int num=0;ll tmp=1;
        rep(j,0,i)
        {
            if((1<<j)&i)
            {
                ++num;
                tmp*=data[j];
            }
            if((1<<j)>i)
                break;
        }

        if(num&1)
        {
            ll t=(n-1)/tmp;
            if(t%2==0)
                ans+=((t/2*(t+1))%mod)*tmp%mod;
            else
                ans+=(((t+1)/2)*t%mod)*tmp%mod;
        }
        else
        {
            ll t=(n-1)/tmp;
            if(t%2==0)
                ans-=((t/2*(t+1))%mod)*tmp%mod;
            else
                ans-=(((t+1)/2)*t%mod)*tmp%mod;
        }
    }
    //cout<<n<<" "<<(ans+mod)%mod<<endl;
    cout<<(ans%mod+mod)%mod<<endl;
}
int main(int argc, const char * argv[])
{
    Prime();
    while(cin>>n,n)
    {
        solve();
    }
    return 0;
}

第二种 比第一种简单:我们先求出 与n互质的sum ,用后用n*(n+1)/2-sum就是我们要求的答案了,sum可以根据欧拉函数的应用就可以求, 即 sum=n*phi(n)/2;

首先我们先证明   如果 gcd(i,n)=1那么 gcd(n-i,n)=1.

我们可以用反证法,设 gcd(i,n)=1,gcd(n-i,n)!=1, 因为  gcd(n-i,n)=k,k>1 ,我们可以知道 k整除n,同时 k整除(n-i),有以上所知再通过整除的性质,k也整除i,与gcd(i,n)=1矛盾  证毕。

所以我们知道  i与n互质,n-i也与n互质 ,所以他们的和 为n,我们就可以n*互质的组数=sum,组数 就可以欧拉函数 可以求出 phi(n)/2,所以可以得到上式 sum=n*phi(n)/2;

代码:

#include <iostream>
#include <cstring>
#include <cmath>
#define rep(i,u,o) for(int i=(int)(u);i<=(int)(o);++i)
using namespace std;
typedef long long ll;
ll n,m;int tol;
const ll mod=1e9+7;
ll phi()
{
    ll rea=n;
    rep(i,2,sqrt(n))
    {
        if(n%i==0)
        {
            rea=rea-rea/i;
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        rea=rea-rea/n;
    return rea;
}
int main(int argc, const char * argv[])
{
    while(cin>>n,n)
    {
        m=n;
        cout<<(((m*(m-1)/2)%mod-(phi()*m/2)%mod)+mod)%mod<<endl;
    }
    return 0;
}

hdu 4407题目传送门: 点击打开链接

题意:求一个区间 [x,y]与p互质sum,其中区间的数可以改动。

思路:这题就是上题的思路,然后加了几步改动数据,求区间的sum,首先我们可以看到题中的m<=1000,也就是说 改动的操作很少 ,所以我们先保存改动的操作,然后求区间的和的时候我们先求出原来[x,y]的符合题意的sum,这里我们可以用上题的思路求和  ,我们知道[x,y] 的sum=[1,y]的sum-[1,x-1]的sum 就可以了 ,那么对于这个区间改动的数据怎么办,我们可以暴力判断 是否符合条件 反正m<=1000。

代码:

#include <iostream>
#include <cstring>
#include <map>
#define  rep(i,o,u) for(int i=(int)(o);i<=(int)(u);++i)
#define  rrep(i,o,u) for(int i=(int)(o);i>=(int)(u);--i)
#define  clr(a,x)  memset(a,(x),sizeof(a))
using namespace std;
typedef long long  ll;
const int maxn=1e3+100;
const int MAXSIZE=1e3;
bool Mark[MAXSIZE+100];
int prime[maxn];
int inde=0,num;ll data[100];
ll n,m;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
void Prime()
{
    clr(Mark,0);
    rep(i,2,MAXSIZE)
    {
        if(!Mark[i])
            prime[inde++]=i;
        rep(j,0,inde-1)
        {
            if(prime[j]*i>=MAXSIZE)
                break;
            Mark[i * prime[j]] = 1;
            if(i%prime[j]==0)
                break;
        }
    }
}
void fprime(ll a)
{
    num=0;
    rep(i,0,inde-1)
    {
        if(prime[i]*prime[i]>a)
            break;
        if(a%prime[i]==0)
        {
            data[num++]=prime[i];
            while(a%prime[i]==0)
                a/=prime[i];
        }
    }
    if(a!=1)
        data[num++]=a;
}
void solve()
{
    map<ll,ll> mp;
    ll t,a,b;
    cin>>n>>m;
    while(m--)
    {
        cin>>t;
        if(t==2)
        {
            cin>>a>>b;
            mp[a]=b;
        }
        else
        {
            ll p;
            cin>>a>>b>>p;
            fprime(p);
            int e=1<<num;ll ans=0;
            rep(i,1,e-1)
            {
                int nn=0;ll tt=1;
                rep(j,0,i)
                {
                    if((1<<j)>i)
                        break;
                    if((1<<j)&i)
                    {
                        ++nn;
                        tt*=data[j];
                    }
                }
                if(nn&1)
                {
                    ll b1=(ll)b/tt,a1=(ll)(a-1)/tt;
                    ans=ans+b1*(b1+1)/2*tt-a1*(a1+1)/2*tt;
                }
                else
                {
                    ll b1=b/tt,a1=(a-1)/tt;
                    ans=ans-b1*(b1+1)/2*tt+a1*(a1+1)/2*tt;
                }
               // cout<<ans<<endl;
            }
            ans=(b-a+1)*(a+b)/2-ans;
            for(auto it=mp.begin();it!=mp.end();++it)
            {
               // cout<<it->first<<" "<<it->second<<endl;
                if(it->first>=a&&it->first<=b)
                {
                    if(it->first>b)
                        break;
                    if(gcd(it->first,p)==1)
                        ans-=it->first;
                    if(gcd(it->second,p)==1)
                    ans+=it->second;
                }
            }
            cout<<ans<<endl;
        }
    }
}
int main(int argc, const char * argv[])
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    Prime();
    while(t--)
    {
        solve();
    }
    return 0;
}

hdu 4059题目传送门: 点击打开链接

题意:求与每个n互质的四次方的和

思路:这题就是3501 的变形 ,我们先算  不互质的四次方和,然后总的-sum就可以,3501是提出p 这里我们可以提出p^4 .这里我们要知道 1+2^4+3^4..  的求和公式

即n*(n+1)*(2*n+1)*(3*n^2+3*m-1)/30    因为取余 我们还要求出 30的逆元就可以了

代码:

#include <iostream>
#include <cstring>
#include <cmath>
#define rep(i,o,u) for(int i=(int)(o);i<=(int)(u);++i)
#define clr(a,x) memset(a,(x),sizeof(a))
using namespace std;
typedef long long  ll;
const int MAXSIZE=1e5;
const int maxn=1e5;
const int mod=1e9+7;
bool Mark[MAXSIZE+100];
int prime[maxn];
int inde=0,num;ll data[100];
ll xx,yy;
void ex_gcd(ll a,ll b,ll &x,ll& y)
{
    if(!b)
    {
        x=1,y=0;
        return;
    }
    ll x1,y1;
    ex_gcd(b,a%b,x1,y1);
    x=y1,y=x1-(a/b)*y1;
}
void Prime()
{
    clr(Mark,0);
    rep(i,2,MAXSIZE)
    {
        if(!Mark[i])
            prime[inde++]=i;
        rep(j,0,inde-1)
        {
            if(prime[j]*i>=MAXSIZE)
                break;
            Mark[i * prime[j]] = 1;
            if(i%prime[j]==0)
                break;
        }
    }
}
void fprime(int a)
{
    num=0;
    rep(i,0,inde-1)
    {
        if(prime[i]*prime[i]>a)
            break;
        if(a%prime[i]==0)
        {
            data[num++]=prime[i];
            while(a%prime[i]==0)
                a/=prime[i];
        }
    }
    if(a!=1)
        data[num++]=a;
}
void solve()
{
    ll n;
    cin>>n;
    if(n==1)
    {
        cout<<0<<endl;
        return;
    }
    fprime((int)n);
    ll ans=0;
    int e=1<<num;
    rep(i,1,e-1)
    {
        ll tmp=1;int nn=0;
        rep(j,0,i)
        {
            if((1<<j)>i)
                break;
            if((1<<j)&i)
            {
                ++nn;
                tmp*=data[j];
            }
        }
        ll tt=1;
        rep(j,0,3)
            tt=tt*tmp%mod;
        ll m=n/tmp;
        tmp=(((((m%mod)*(m+1)%mod)*((2*m%mod)+1)%mod)*((3*m%mod*m%mod)+3*m%mod-1)%mod)*xx%mod)*tt%mod;
        if(nn&1)
            ans=(ans+tmp)%mod;
        else
            ans=(ans-tmp)%mod;
    }
    ll sum=((((n%mod)*(n+1)%mod)*((2*n%mod)+1)%mod)*((3*n%mod*n%mod)+3*n%mod-1)%mod)*xx%mod;
    //cout<<(sum+mod)%mod<<endl;
    cout<<((sum-ans)%mod+mod)%mod<<endl;
}
int main(int argc, const char * argv[])
{
    ios::sync_with_stdio(false);
    Prime();
    ex_gcd((ll)30,(ll)mod,xx,yy);
    xx=((xx%mod)+mod)%mod;
    int t;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值