• 【题解】 [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;
    }
    
  • 相关阅读:
    字体辉光效果
    C# 读写 Photoshop PSD文件 操作类
    SQL Server日期时间格式转换字符串详解
    用DataTable填充实体类List
    C#控件的闪烁问题解决方法总结
    .NET Framework的属性类对控件的支持功能
    Aspose破解版本dll
    整理的C#屏幕截图,控件截图程序
    C#绘制渐变背景
    VS2010编译的时候出现fatal error LNK1146: 没有用选项“/out:”指定的参数
  • 原文地址:https://www.cnblogs.com/imakf/p/13775742.html
Copyright © 2020-2023  润新知