• @51nod



    @description@

    C(M,N) = M! / N! / (M - N)! (组合数)。

    给出M和质数p,求C(M,0), C(M,1)......C(M,M)这M + 1个数中,有多少数不是p的倍数,有多少是p的倍数但不是p^2的倍数,有多少是p^2的倍数但不是p^3的倍数......。

    原题传送门。

    @solution@

    考虑一个阶乘 n! 含多少 p 因子,容易发现含 (sum_{i=1}lfloorfrac{n}{p^i} floor) 个。

    因此,组合数 C(M, N) 含因子 p 共 (sum_{i=1}(lfloorfrac{M}{p^i} floor - lfloorfrac{N}{p^i} floor - lfloorfrac{M - N}{p^i} floor)) 个。

    而上式又可以写作 (sum_{i=1}[N mod p^i + (M - N) mod p^i geq M mod p^i])。或者更通俗地说,N + (M - N) 在 p 进制下的进位次数。

    因此直接从低位开始数位 dp,记录一下上一次有没有进位即可。

    @accepted code@

    #include <cstdio>
    #include <cassert>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    ll dgt[66], M, P; int cnt;
    void get_dight() {
    	cnt = 0;
    	for(ll i=M;i;i/=P)
    		dgt[++cnt] = i % P;
    }
    ll fun(ll x) {
    	return max(0LL, min(P - 1, x) - max(0LL, x - P + 1) + 1);
    }
    ll f[2][66], g[2][66];
    void solve() {
    	scanf("%lld%lld", &M, &P), get_dight();
    	
    	f[0][0] = 1, f[1][0] = 0;
    	for(int i=1;i<=cnt;i++) {
    		for(int j=0;j<i;j++)
    			for(int o=0;o<=1;o++)
    				g[o][j] = f[o][j], f[o][j] = 0;
    		f[0][i] = f[1][i] = 0;
    		for(int o=0;o<=1;o++) {
    			ll p = dgt[i] - o;
    			for(int j=0;j<i;j++) f[0][j] += fun(p)*g[o][j];
    			p = dgt[i] + P - o;
    			for(int j=0;j<i;j++) f[1][j + 1] += fun(p)*g[o][j];
    		}
    	}
    	
    	while( f[0][cnt - 1] == 0 ) cnt--;
    	
    	ll sum = 0;
    	for(int i=0;i<cnt;i++)
    		printf("%lld%c", f[0][i], i + 1 == cnt ? '
    ' : ' '), sum += f[0][i];
    	assert(sum == M + 1);
    }
    
    int main() {
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    

    @details@

    51nod 搬题的时候并没有附上原题关于输出格式的说明。事实上本题需要舍去末尾多余的 0。

    本题所用的结论其实叫作 “库默尔定理”,不过定理名字也不是很重要。

  • 相关阅读:
    找水王
    用户体验评价
    人件集阅读笔记01
    第十四周学习进度
    第十三周学习进度
    第十二周学习进度
    第十一周学习进度
    梦断代码阅读笔记03
    团队项目-第一阶段冲刺-10
    Shell按行读取文件的3种方法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12447393.html
Copyright © 2020-2023  润新知