zoukankan      html  css  js  c++  java
  • Luogu2481 SDOI2010 代码拍卖会 DP、组合

    传送门


    神仙DP

    注意到(N leq 10^{18}),不能够直接数位DP,于是考虑形成的(N)位数的性质。

    因为低位一定不会比高位小,所以所有满足条件的(N)位数一定是不超过(9)(f(x)(x in [1,N]))的和,其中(f(x) = sumlimits_{i=0}^{x-1} 10^i),且其中一定有一个(f(N))

    考虑由(f(x) mod P)形成的数列,因为(f(x) = 10f(x-1) + 1),所以这个数列一定会存在一个不超过(P)的循环节。那么我们可以通过这个预处理出(cnt_i = sumlimits_{x=1}^N[f(x) mod P = i]),同时求出(f(N) mod P)的值。

    接下来就可以DP了:设(f_{i,j,k})表示考虑了(cnt_0 sim cnt_{i-1}),选择了(k)(f(x)),它们的和(mod P = j)的方案数。转移考虑枚举(cnt_i)中选择多少个,这就是一个插板法,转移系数是一个组合数。

    最后的答案就是(sumlimits_{i=0}^8 f_{P,(P - f(N)) mod P,i})(i)最大为(8)的原因是必须要选择一个(f(N))

    #include<bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int MOD = 999911659;
    int dp[503][503][9] , Cnt[503] , dir[503] , N , P;
    
    int poww(int a , int b){
    	int times = 1;
    	while(b){
    		if(b & 1) times = times * a % MOD;
    		a = a * a % MOD; b >>= 1;
    	}
    	return times;
    }
    
    int binom(int a , int b){
    	int times = 1;
    	for(int i = a ; i > a - b ; --i)
    		times = times * i % MOD * poww(a - i + 1 , MOD - 2) % MOD;
    	return times;
    }
    
    signed main(){
    	cin >> N >> P;
    	int cur = 1 % P , cnt = 1 , tmp = 1 % P , ed;
    	do{dir[cur] = cnt; ++cnt; cur = (cur * 10 + 1) % P;}while(!dir[cur]);
    	for(int i = 1 ; i < dir[cur] && i <= N ; ++i , tmp = (tmp * 10 + 1) % P) ++Cnt[ed = tmp];
    	if(dir[cur] <= N){
    		for(int i = dir[cur] ; i < cnt ; ++i , tmp = (tmp * 10 + 1) % P) Cnt[ed = tmp] = (N - dir[cur] + 1) / (cnt - dir[cur]) % MOD;
    		for(int i = 1 ; i <= (N - dir[cur] + 1) % (cnt - dir[cur]) ; ++i , tmp = (tmp * 10 + 1) % P) ++Cnt[ed = tmp];
    	}
    	dp[0][0][0] = 1;
    	for(int i = 0 ; i < P ; ++i)
    		for(int j = 0 ; j <= 8 ; ++j){
    			int val = binom(Cnt[i] + j - 1 , j);
    			if(!val) continue;
    			for(int k = 0 ; k < P ; ++k)
    				for(int l = 0 ; l + j <= 8 ; ++l)
    					dp[i + 1][(k + i * j) % P][l + j] = (dp[i + 1][(k + i * j) % P][l + j] + val * dp[i][k][l]) % MOD;
    		}
    	int sum = 0;
    	for(int i = 0 ; i <= 8 ; ++i)
    		sum = (sum + dp[P][(P - ed) % P][i]) % MOD;
    	cout << sum;
    	return 0;
    }
    
  • 相关阅读:
    如何查看自己的显卡是否支持DirectX 12
    笔记本屏幕忽然变暗的解决办法
    Python 自动给数字前面补0
    vscode左边侧边栏字体的大小
    .NET 异步详解
    新版 C# 高效率编程指南
    轻松学会 React 钩子:以 useEffect() 为例
    React Hooks 入门教程
    UMI.js开发知识总结
    处理react项目ie11浏览器运行空白问题
  • 原文地址:https://www.cnblogs.com/Itst/p/11295096.html
Copyright © 2011-2022 走看看