zoukankan      html  css  js  c++  java
  • 【题解】 [NOI Online #1 入门组]跑步 dp+根号分治+拆分数 Luogu6189

    Legend

    Link ( extrm{to Luogu})

    (n) 的拆分数。

    (1 le n le 10^5)

    Editorial

    Simple (O(n sqrt{n}))

    考虑到一个事实:(le sqrt{n}) 的数字只有 (O(sqrt{n})) 个,可以暴力完全背包。

    完全背包显然没有人不会。复杂度 (O(n sqrt{n}))

    考虑到另一个事实:(>sqrt{n}) 的数字最多只会被选 (O(sqrt{n})) 个,可以暴力 (dp)

    怎么 (dp)?设 (dp_{i,j}) 为把 (j) 拆分成 (i) 个数字的方案数,总状态数是 (O(n sqrt{n})) 的。

    转移有 (dp_{i,j}=dp_{i-1,j-(sqrt{n}+1)}+dp_{i,j-i}) 两种。

    含义分别为选择一个 (sqrt{n}+1) 大小的物体和把所有物体高度增加 (1)。这样可以涵盖所有情况。

    这一部分复杂度也是 (O(n sqrt{n}))

    再用 (O(n)) 合并两个背包的答案,总复杂度 (O(n sqrt{n}))

    $O(n log n) $

    一切的前提,模数要是 ( m{NTT}) 模数。显然本题用不了这个做法

    考虑毒瘤的生成函数做法:

    拆分数的生成函数显然为 (F(x)=prodlimits_{i=1}^{infty} sumlimits_{j=0}^{infty}(x^i)^j)

    根据 (sum_{i=0}^{infty} x^i=frac{1}{1-x}) 可以得出 (F(x)=prodlimits_{i=1}^{infty} dfrac{1}{1-x^i})

    两边同时取 (ln)(ln F(x)= -sumlimits_{i=1}^{infty}ln (1-x^i))

    现在的问题就是如何计算 (h(x)=ln(1-x)),因为 (ln F(x)= -sumlimits_{i=1}^{infty}h(x^i))

    求个导数 (h'(x)=-frac{1}{1-x}=-sumlimits_{i=0}^{infty}x^i)

    同时积分有 (h(x)= - sumlimits_{i=1}^{infty}frac{x^i}{i}),求完了。

    带回 (F(x))(ln F(x)= sumlimits_{i=1}^{infty}sumlimits_{j=1}^{infty}frac{x^{ij}}{j}=sumlimits_{t=1}^{infty}x^t sumlimits_{j|t}frac{1}{j})

    显然这个式子可以调和级数 (O(n log n)) 预处理完。

    再把式子 (exp) 回去就做完了。复杂度就是 (O(n log n))

    Code

    采用的根号分治做法。

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    
    using namespace std;
    
    int n ,p;
    
    const int MX = 1e5 + 23;
    const int SIZE = 320;
    int dp[MX];
    int f[SIZE][MX] ,dp2[MX];
    
    int main(){
    	cin >> n >> p;
    	dp[0] = 1 % p;
    	for(int i = 1 ; i <= min(n ,SIZE) ; ++i){
    		for(int j = i ; j <= n ; ++j){
    			dp[j] = (dp[j] + dp[j - i]) % p;
    		}
    	}
    
    	dp2[0] = f[0][0] = 1 % p;
    	for(int i = 1 ; i < SIZE ; ++i){
    		for(int j = SIZE + 1 ; j <= n ; ++j){
    			f[i][j] = (f[i - 1][j - (SIZE + 1)] + f[i][j - i]) % p;
    			dp2[j] = (dp2[j] + f[i][j]) % p;
    		}
    	}
    
    	int Ans = 0;
    	for(int i = 0 ; i <= n ; ++i){
    		Ans = (Ans + 1LL * dp[i] * dp2[n - i]) % p;
    	}
    	printf("%d
    " ,Ans);
    	return 0;
    }
    
  • 相关阅读:
    11Java网络编程
    Java字节流与字符流
    10缓冲流、转换流、序列化流、Files
    JavaFile类和递归
    Java线程池 与Lambda
    Java多线程与线程同步
    5Java异常处理
    php base64数据与图片的转换
    PHP的json_encode中文被转码的问题
    设置MySQL允许外网访问
  • 原文地址:https://www.cnblogs.com/imakf/p/13775742.html
Copyright © 2011-2022 走看看