• [NOI Online #1 入门组]跑步


    description:

    求整数n的划分数模p的值

    data range:

    (Nle 10^5) (p<2^{30})

    solution:

    十分巧妙的题目
    很显然地这是一道dp的题目
    首先设(f_{i,j})表示把整数i分为若干个不超过j的数的方案数
    那么就有(f_{i,j}=f_{i-j,j}+f_{i,j-1})
    就是说要么拆分出j来,要么把上限降为j-1
    可以发现这是一个完全背包的形式,于是可以把j的那一维去掉优化空间为(O(n))
    但是时间复杂度仍然为(O(n^2))考虑如何优化
    还有另一种dp的方法:
    (g_{i,j})表示将整数i恰好划分为j个不为零的数的方案数
    那么就有(g_{i,j}=g_{i-1,j-1}+g_{i-j,j})
    就是说,对于当前的位置要么放一个1后再也不放了,要么给现在的所有位置都加上1
    虽然这个dp本质上仍然是(O(n^2)),但是结合二者我们就可以有神奇的做法
    (m=sqrt n)
    对于(j<m)计算出所有的(f_{i,j})
    然后把(g_{i,j})的定义魔改为用j个大于等于m的数和为i的方案数
    那么有类似转移:(g_{i,j}=g_{i-1,j-m}+g_{i-j,j})
    这样可以求出所有(g_{i,j})
    那么最后的答案是什么呢?
    可以发现(f,g)是没有关系的,因此答案就是(sum_{i=0}^nf_{i,m-1}*sum_{j=0}^mg_{n-i,j})

    time space complexity:

    time&space:(O(n^{frac{3}{2}}))

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5,SQRT=350;
    int m,n,p;
    int f[N],g[N][SQRT];  
    inline int add(int x,int y){x+=y;return x>=p?x-p:x;}
    int main()
    {
    	scanf("%d%d",&n,&p);
    	m=sqrt(n)+1;
    	f[0]=1;
    	for(int i=1;i<m;++i)
    		for(int j=i;j<=n;++j)
    			f[j]=add(f[j],f[j-i]);
    	g[0][0]=1;
    	for(int i=1;i<m;++i)
    		for(int j=i;j<=n;++j)
    		{
    			g[j][i]=g[j-i][i];
    			if(j>=m)g[j][i]=add(g[j][i],g[j-m][i-1]);
    		}
    	int ans=0;
    	for(int i=0;i<=n;++i)
    	{
    		int anss=0;
    		for(int j=0;j<m;++j)anss=add(anss,g[n-i][j]);
    		ans=add(ans,1ll*anss*f[i]%p);
    	}
    	cout<<ans%p;
    	return 0;
    }
    
  • 相关阅读:
    1036 Boys vs Girls (25 分)
    1090 Highest Price in Supply Chain (25 分)
    1106 Lowest Price in Supply Chain (25 分)dfs
    设计模式-解释器模式
    设计模式-命令模式
    设计模式-责任链模式
    设计模式-代理模式
    设计模式-享元模式
    设计模式-外观模式
    设计模式-装饰器模式
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13839605.html
Copyright © 2020-2023  润新知