清华集训2016 组合数问题

  • 【清华集训2016】组合数问题
  • 其实卢卡斯定理是一个(K)进制拆分的过程。
  • 考虑每一次卢卡斯迭代的过程,实际上就是把最后一位数单独拿出来算,然后把最后一位去掉。
  • 那么如果我们把两个位置上的数都拆分的话,那么就相当于每一个位置上的组合数乘起来。

[C_{p,q}=prod_{ileq p,jleq q} C_{i,j} ]

  • 所以问题就变成了,有多少个(ileq n,jleq m)的数对((i,j)),满足在(K)进制拆分下的某一位数(i<j)
  • 这个就比较套路了,直接数位(dp)即可,设(f_{i,01,01,01})表示当前在(i)位,是否满足要求,(i)的填法是否危险,(j)的填法是否危险,记忆化即可。
  • 复杂度(O(8*log_kn))
#include<bits/stdc++.h>
#define R register int
#define ll long long 
using namespace std;
const int N=100011;
const int mod=1e9+7;
const int inv=500000004;
int t,K,l1,l2,Dc,O1[N],O2[N],C[201][201];
int f[2][2][2][N];
ll n,m;
int gi(){
    R x=0,k=1;char c=getchar();
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-')k=-1,c=getchar();
    while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*k;
}
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int Dfs(R is,R q,R p,R i){
	if(!i)return is;
	if(f[is][p][q][i]!=-1)return f[is][p][q][i];
	R &nw=f[is][p][q][i],lm1=(q?(K-1):O1[i]),lm2=(p?(K-1):O2[i]);
	nw=0;
	for(R j=0;j<=lm1;++j)
		for(R k=0;k<=lm2;++k)
			add(nw,Dfs(is|(j<k),q|(j<lm1),p|(k<lm2),i-1));
	return f[is][p][q][i];
}
void sol(){
	scanf("%lld %lld",&n,&m);
	l1=l2=0,m=min(n,m),Dc=m%mod;
	memset(f,-1,sizeof(f));
	while(n)O1[++l1]=(n%K),n/=K;
	while(m)O2[++l2]=(m%K),m/=K;
	while(l2<=l1)O2[++l2]=0;
	R ans=Dfs(0,0,0,l1);
	add(ans,mod-(1ll*(Dc+1)*Dc%mod*inv%mod));
	printf("%d
",ans);
}
int main(){
	t=gi(),K=gi();
	while(t--)sol();
	return 0;
}

原文地址:https://www.cnblogs.com/Tyher/p/10051430.html