zoukankan      html  css  js  c++  java
  • UOJ 275. 【清华集训2016】组合数问题

    UOJ 275. 【清华集训2016】组合数问题

    组合数 $C_n^m $表示的是从 (n) 个物品中选出 (m) 个物品的方案数。举个例子,从$ (1,2,3)(1,2,3)$ 三个物品中选择两个物品可以有 ((1,2),(1,3),(2,3)) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数$ C_m^n$的一般公式:

    [C_n^m=frac{n!}{m!(n-m)!} ]

    其中 (n!=1×2×⋯×n)。(额外的,当 n=0n=0 时, n!=1n!=1)

    小葱想知道如果给定$ n,m$ 和 (k),对于所有的 (0≤i≤n,0≤j≤min{i,m})有多少对 ((i,j)) 满足 (C_i^j)(k) 的倍数。

    答案对 (10^9+7) 取模。

    输入格式

    第一行有两个整数 (t,k)其中 (t) 代表该测试点总共有多少组测试数据。

    接下来 (t) 行每行两个整数 (n,m)

    输出格式

    (t) 行,每行一个整数代表所有的 (0leq ileq n,0leq jleq min left { i, m ight }) 中有多少对$ (i,j)(满足)C_i^j$是 (k) 的倍数。

    限制与约定

    对于(100\%) 的测试点, (1leq n,mleq 10^{18},1 leq t,kleq 100),且 (k) 是一个质数。

    (\)
    首先考虑使用卢卡斯定理:

    [ ext{Lucas}(n,m)mod k= ext{Lucas}(frac{n}{k},frac{m}{k})cdotinom{n\%k}{m\%k}mod k ]

    迭代过程中只要有一位上的(inom{n\%k}{m\%k}=0)那么最后的组合数就是(k)的倍数。当(n<k,m<k)时,只有(n<m)的情况下:(inom{n}{m}=0)

    我们将(n,m)写成(k)进制的数,然后做数位(DP)。先不考虑(jleq i)的限制的话要好做一些,然后在减掉(j>i)的情况(这部分显然为0)就好了。

    代码(小心爆(long long)):

    #include<bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=1e9+7;
    ll n,m,k;
    ll A[100],B[100];
    
    int d;
    #define pr pair<ll,ll>
    #define mp(a,b) make_pair(a,b)
    
    pr f[100][2][2];
    pr dfs(int v,int flag1,int flag2) {
    	if(v<0) return mp(0,1);
    	if(f[v][flag1][flag2].first!=-1) return f[v][flag1][flag2];
    	int u1=(!flag1)?k-1:A[v],u2=(!flag2)?k-1:B[v];
    	ll ans0=0,ans1=0;
    	pr now;
    	for(int i=0;i<=u1;i++) {
    		for(int j=0;j<=u2;j++) {
    			now=dfs(v-1,flag1&&i==u1,flag2&&j==u2);
    			if(i<j) {
    				(ans0+=1ll*now.first+now.second)%=mod;
    			} else {
    				(ans0+=now.first)%=mod;
    				(ans1+=now.second)%=mod;
    			}
    		}
    	}
    	f[v][flag1][flag2]=mp(ans0,ans1);
    	return mp(ans0,ans1);
    }
    
    ll cal(ll l,ll r) {return 1ll*(l+r)*(r-l+1)/2%mod;}
    
    int main() {
    	int T=Get();
    	k=Get();
    	while(T--) {
    		n=Get(),m=Get();
    		d=0;
    		ll mx=max(n,m);
    		while(mx) {
    			d++;
    			mx/=k;
    		}
    		d--;
    		ll x=n;
    		for(int i=0;i<=d;i++) {
    			A[i]=x%k;
    			x/=k;
    		}
    		x=m;
    		for(int i=0;i<=d;i++) {
    			B[i]=x%k;
    			x/=k;
    		}
    		for(int i=0;i<=d;i++)
    			for(int a=0;a<=1;a++)
    				for(int b=0;b<=1;b++) f[i][a][b]=mp(-1,-1);
    		pr ans=dfs(d,1,1);
    		ans.first=(ans.first-cal(max(1ll,m-n)%mod,m%mod)+mod)%mod;
    		cout<<ans.first<<"
    ";
    	}	
    	return 0;
    }
    
    
  • 相关阅读:
    sql server 检测是否更新并输出更新的数据
    SQL Cross Join
    使用editplus删除 telepro的标记
    201671010104 初学Java的感想以及认知
    201671010104学习Java心得
    201671010104学习Java程序设计进度条
    从 URL 调用 WebService
    初识 Adobe AIR
    Adobe AIR 初体验:第一个Adobe AIR 的项目
    Float元素父容器在Firefox中自动撑大的方法
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10753140.html
Copyright © 2011-2022 走看看