[CSP校内集训]2019.10.16数学专题

\(T1:facsum\)

题目

Mr.Hu最近偶得一函数\(f(n) = n^m \times \sum_{d|n}{\sigma_0(d)\mu(\frac{n}{d})\frac{n}{d}}\)

\(F(n) = \sum_{i=1}^n{f(i)},(n\leq 10^7,m\leq 10)\),答案对\(10^9+7\)取模

做法

数论函数掠杀我qwq

可以看出右边那个\(\sum\)里面的三个部分都是积性函数,所以它们的乘积也是积性函数,通过计算/打表/瞎猜可知:

\(p\)为质数时,有\(f(p) = 2-p\)

对于\(p^x\),有\(f(p^x) = -x*p + x + 1\)

线性筛一遍即可求解

Code

#include<bits/stdc++.h>
#define N 10000005
#define re register
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int n,m;
int p[N],d[N],c[N],v[N],cnt;
bool isnotp[N];

template <class T>
void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
ll calc(ll x,ll y) {return ((ll)x-x*y+mod+1)%mod;}
void init(int maxn)
{
    c[1]=v[1]=1;
    isnotp[1]=1;
    for(re int i=2;i<=maxn;++i)
    {
        if(!isnotp[i]) p[++cnt]=i,c[i]=i,d[i]=1,v[i]=2-i;
        for(re int j=1;j<=cnt&&(ll)p[j]*i<=maxn;++j)
        {
            isnotp[p[j]*i]=1;
            if(i%p[j]==0)
            {
                c[p[j]*i]=(ll)c[i]*p[j]%mod;
                d[p[j]*i]=d[i]+1;
                v[p[j]*i]=v[i/c[i]]*calc(d[p[j]*i],p[j])%mod;
                break;
            }
            c[p[j]*i]=p[j];
            d[p[j]*i]=1;
            v[p[j]*i]=(ll)v[i]*v[p[j]]%mod;
        }
    }
}
ll quickpow(ll a,ll b)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
int main()
{
    freopen("facsum.in","r",stdin);
    freopen("facsum.out","w",stdout);
    init(N-1);
    read(n);read(m);
    ll ans=0;
    for(re int i=1;i<=n;++i)
    {
        ans=(ans+quickpow(i,m)*v[i])%mod;
    }
    cout<<(ans%mod+mod)%mod<<endl;
    return 0;
}

\(T2:group\)

题目

Mr.Hu在研究等比数列,他想知道\(a^{1,2.....}\)这样的一个无穷数列对于模\(mod\)有多少项本质不同

做法

如果出现了一个\(a^n\)使得\(a^n \equiv a\),那么\(1\)\(n-1\)这一段一定就是所有本质不同的项(因为之后的项会循环了),现在就是要求这样一个最小循环节的大小

一般情况下,问题变成解高次同余方程\(a^x \equiv 1\),这里的\(x\)即答案,用BSGS即可

需要注意的是,同余方程可能无解,但\(x\)一定有解(雾),需要特判(模数为1的情况)

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 100007;
int T;
ll a,mod;
struct List
{
    int next,to;
    ll val;
}l[M<<3]; int hd[M+5],cnt=0; 
template <class T>
void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
ll quickpow(ll a,ll b)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
void ins(ll val,int loc)
{
    ll k=val%M;
    for(int i=hd[k];i;i=l[i].next) if(l[i].to==loc && l[i].val==val) return;
    l[++cnt].next=hd[k];
    l[cnt].to=loc;
    l[cnt].val=val;
    hd[k]=cnt;
}
ll query(ll val)
{
    ll k=val%M;
    for(int i=hd[k];i;i=l[i].next) if(l[i].val==val) return l[i].to;
    return -1;
}
ll BSGS(ll a,ll b,ll mod)
{
    int p=(int)sqrt(mod)+1;
    ll now=1;
    for(int i=0;i<p;++i)
    {
        ins(now,i);
        now=now*a%mod;
    }
    a=quickpow(a,p);
    now=a;
    for(int i=1;i<=p;++i)//从1开始循环避开a^1
    {
        int j=query(now);
        if(j>=0&&i*p-j>=0) return i*p-j;
        now=now*a%mod;
    }
    return -1;
}

int main()
{
    freopen("group.in","r",stdin);
    freopen("group.out","w",stdout);
    read(T);
    while(T--)
    {
        read(a);read(mod);
        memset(hd,0,sizeof(hd));
        cnt=0;
        printf("%lld\n",BSGS(a,1,mod));
    }
    return 0;
}

\(T3:ccount\)

题目

Mr.Hu在学习美丽的组合数,他想求\(\sum_{i=l}^{r}{[C_n^i\% 5=0]},(n,l,r\leq 10^18)\)

做法

看到\(n\)那么大还要求组合数,显然\(lucas\)定理,计数问题选择数位DP,用\(lucas\)相当于将\(n\)看做5进制数了,相当于在5进制下做数位DP

如果想到了变为5进制,本题其实是个相当模板的数位DP

Code

#include<bits/stdc++.h>
#define N 100
#define wtcl 0
using namespace std;
typedef long long ll;
int T,C[6][6];
int wei[N],cnt;
int nn[N],sum;
ll n,l,r;
ll f[N][2][2];

template <class T>
void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
void init(ll x)
{
    ll c=x;
    cnt=0;
    while(c) {wei[++cnt]=c%5;c/=5;}
}
ll dfs(int step,bool ok,bool lim)//处理到第step位,是否出现了0,顶上界 
{
    if(step==0) return ok;
    if(f[step][ok][lim]!=-1) return f[step][ok][lim];
    ll ret=0;
    if(lim)
    {
        for(int i=0;i<wei[step];++i)
        {
            int p=C[nn[step]][i];
            ret+=dfs(step-1,ok|(p%5==0),0); 
        }
        int p=C[nn[step]][wei[step]];
        ret+=dfs(step-1,ok|(p%5==0),1);
    }
    else
    {
        for(int i=0;i<5;++i)
        {
            int p=C[nn[step]][i];
            ret+=dfs(step-1,ok|(p%5==0),0);
        }
    }
    return f[step][ok][lim]=ret;
}
ll solve(ll x)//找出所有小于等于x的数满足C(n,i)%5=0 
{
    if(x==-1) return 0;
    memset(wei,0,sizeof(wei));
    memset(f,-1,sizeof(f));
    init(x);//把x拆了 
    return dfs(sum,0,1);
}
int main()
{
    freopen("ccount.in","r",stdin);
    freopen("ccount.out","w",stdout);
    C[0][0]=C[1][0]=C[1][1]=1;
    for(int i=2;i<=5;++i)
    {
        C[i][0]=1;
        for(int j=1;j<=i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
    read(T);
    while(T--)
    {   
        read(l);read(r);read(n);
        sum=0;
        memset(nn,0,sizeof(nn));
        while(n) {nn[++sum]=n%5;n/=5;}
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return wtcl;
}
/*
2
1 3 4
1 4 5
*/

猜你喜欢

转载自www.cnblogs.com/Chtholly/p/11687777.html