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