• uoj#275. 【清华集训2016】组合数问题(数位dp)


    传送门

    假设有(k|{nchoose m}),因为(n!)中质因子(k)的次数为(S(n)=leftlfloorfrac{n}{k} ight floor+leftlfloorfrac{n}{k^2} ight floor+...),而(m!)((n-m)!)同理。所以如果(S(n)>S(m)+S(n-m)),那么(k|{nchoose m})

    不难发现,对于每一个(k^i)(leftlfloorfrac{n}{k^i} ight floorgeq leftlfloorfrac{m}{k^i} ight floor+leftlfloorfrac{n-m}{k^i} ight floor)。所以只要有一个(i)使得(leftlfloorfrac{n}{k^i} ight floor>leftlfloorfrac{m}{k^i} ight floor+leftlfloorfrac{n-m}{k^i} ight floor),那么(S(n)>S(m)+S(n-m))

    (i=1)的时候,设(n=ak+b,m=ck+d),则(n-m=(a-c)k+b-d),那么如果(b-d<0)(leftlfloorfrac{n}{k} ight floor=a,leftlfloorfrac{m}{k} ight floor+leftlfloorfrac{n-m}{k} ight floor=c+(a-c-1)=a-1<a),那么就有(S(n)>S(m)+S(n-m))

    同理可得,若(n)(m)(k)进制表示下第(i)位满足(n_i<m_i),那么就有(S(n)>S(m)+S(n-m))

    于是现在的问题就是变成了求(i<n,j<m)(i)(k)进制表示下有某一位数值比(j)小,可以数位dp,这个就不讲了

    然后上面的情况下我们没有考虑(j>i)的情况,因为如果(j>i)那么(k)进制下(i)肯定有某一位小于(j),所以我们对于每个(i)算出它会多算的个数,发现就是一个等差数列求和的形式,带公式就好了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define ll long long
    #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
    #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    ll read(){
        R ll res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    char sr[1<<21],z[20];int C=-1,Z=0;
    inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    void print(R int x){
        if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
        while(z[++Z]=x%10+48,x/=10);
        while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
    }
    const int N=65,P=1e9+7,inv=500000004;
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);
    	return res;
    }
    int l1,l2,num1[N],num2[N],k,p,ans;
    int f[2][2][2][N];ll n,m;
    int dfs(int p,int q,int ok,int pos){
    	if(!pos)return ok;
    	if(~f[p][q][ok][pos])return f[p][q][ok][pos];
    	int res=0,lm1=p?k-1:num1[pos],lm2=q?k-1:num2[pos];
    	fp(i,0,lm1)fp(j,0,lm2)
    			res=add(res,dfs(p|(i<lm1),q|(j<lm2),ok|(i<j),pos-1));
    	return f[p][q][ok][pos]=res;
    }
    void solve(){
    	n=read(),m=read(),l1=l2=0,m=min(n,m),p=m%P;
    	memset(f,-1,sizeof(f));
    	while(n)num1[++l1]=n%k,n/=k;
    	while(m)num2[++l2]=m%k,m/=k;
    	while(l2<=l1)num2[++l2]=0;
    	ans=dfs(0,0,0,l1);
    	ans=dec(ans,1ll*(p+1)*p%P*inv%P);
    	printf("%d
    ",ans);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	int T=read();k=read();
    	while(T--)solve();
    	return 0;
    }
    
  • 相关阅读:
    c#文件读取
    asp.net页面缓存学习
    JQuery学习
    如何在asp.net后台调用前台代码
    c#文件操作二
    oracler主键自动增长
    C#委托学习
    小技巧:DIV中显示字符不完整的解决方法
    英文名字的误区及起名方法
    SharePoint 2010 中提供的母版页
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10241234.html
Copyright © 2020-2023  润新知