题目大意:
首先定义f(n):有n个人参加比赛,可以进行任意轮的比赛,每一轮是将他们分成任意组(必须保证每组人数相同),然后每组的所有人两两之间都必须进行一次比较。f(n)就表示确定第一名所需的最少比较次数。
现在给你:l,r,t,让你求:
分析:
首先说明f(x)的求法:
这也就是等价于,如果一个数x可以质因数分解成:
下面证明该贪心策略的正确性:
首先,证明“假如一个数 x 可以表示成两个整数 q 1 ,q 2 的乘积,那么每组 x 人一定不如首先每组 q 1 人,下一轮每组 q 2 人”:
如果每组x人,那么比赛次数为:
如果先
那么:
这就说明分成两次比赛次数更少。证毕~
其次,证明“假如 q 1 >q 2 ,那么先分成 q 2 组,再分成$$q_1组比赛,总比赛次数更少”:
如果先
如果先
则:
这就说明对于每一轮尽量使每组人数更小才能使得总比赛次数最少。证毕~
综合以上连个结论,我们就得到了贪心策略。
然后就是如何更快的求出一段连续的f(x)的值:
这里有点 类似于线性筛素数的方法,每次枚举当前最小的素数的整数倍,同时对
代码:
#include<bits/stdc++.h>
#define maxn 5000500
#define mod 1000000007
using namespace std;
long long int t;
int l,r;
long long int t_pow[maxn],f[maxn],f_full[maxn];
int is_prime[maxn];
//bool appear[maxn];
void init()
{
for(int i=0;i<maxn;i++)
{
f[i]=0;f_full[i]=(long long int)i;
is_prime[i]=1;
}
}
void init_t()
{
t_pow[0]=1;
for(int i=1;i<=r-l;i++)
{
t_pow[i]=t_pow[i-1]*t;
t_pow[i]%=mod;
//cout<<t_pow[i]<<endl;
}
}
/*void test() { //memset(appear,0,sizeof(appear)); int num=0; for(int i=1;i<=r;i++) { int flag=0; for(int j=2;j<sqrt(i)+1;j++) { if(i%j==0) { flag=1;break; } } if(flag==0) { num++; //appear[i]=1; //cout<<i<<" "; } } cout<<"num="<<num<<endl; } */
void init_f()
{
for(int i=2;i<=r;i++)
{
if(is_prime[i]==1)//i是质数
{
int k=2;
f[i]=(long long int)i*(long long int)(i-1)/2;
f[i]%=mod;
f_full[i]=1;
while(i*k<=r)
{
int temp=i*k;k++;
is_prime[temp]=0;//筛素数
while(f_full[temp]%(long long int )i==0)
{
f_full[temp]/=(long long int)i;
f[temp]+=(((f_full[temp])*(((long long int)i*(long long int)(i-1)/2)%mod))%mod);
f[temp]%=mod;
}
}
}
}
}
int main()
{
scanf("%lld%d%d",&t,&l,&r);
//test();
init();
init_t();
init_f();
long long int ans=0;
for(int i=0;i<=r-l;i++)
{
//printf("t[%d]=%lld f[%d]=%lld f_full[%d]=%lld\n",i,t_pow[i],l+i,f[l+i],i+l,f_full[i+l]);
ans=ans+t_pow[i]*f[l+i];
ans%=mod;
}
printf("%I64d\n",ans);
}