Description
Input
一行两个整数,表示
n,m
n
,
m
。
Output
一行一个整数,表示答案。
Sample Input
3 2
Sample Output
8
Data Constraint
分析:
一开始题目都没有看懂。大概就是说,每个图会被给一个范围在
[1,m]
[
1
,
m
]
的分数,本质相同的图会有相同的分数,因此,只有本质不同的图才可以对答案有贡献,这些图都可以在
[1,m]
[
1
,
m
]
中选一个分数,设本质不同的图有
x
x
个,答案就是。
其实这道题相当于把 n n 个点分成若干个块,两个块不同当且仅当一种大小的块的数目不同(顺序不同不算)。所以这是一个整数划分问题。
整数划分问题有两种,一种是不相等整数划分,即一个整数分成若干个数,这些数必须不同,这种可以用dp解决,因为每个数不相同,所以不会取超过个数。考虑这样构造这个数列,一种是把当前所有的数加
1
1
,第二种在最后面加一个,因为要不相同,所以二操作前要加一个一操作。我们设
f[i][j]
f
[
i
]
[
j
]
为分成
i
i
个数,和为的方案数。对于操作一,相当于从
f[i][j−i]
f
[
i
]
[
j
−
i
]
转移;对于操作二,给前
i−1
i
−
1
个数都加
1
1
,再给当前位加一共要加
i
i
,所以相当于从转移,所以,
时间复杂度 O(nn−−√) O ( n n ) ,空间复杂度 O(n) O ( n ) 。可以 O(1) O ( 1 ) 解决多组询问。
第二种是相等整数划分,也就是这些数时可以相同的,直接暴力完全背包时间太太,不能接受。
我们可以分段dp,因为小于等于
n−−√
n
的,只有
n−−√
n
种,大于
n−−√
n
的,只能取
n−−√
n
个。对于第一种,直接
O(nn−−√)
O
(
n
n
)
完全背包;对于第二种类似于不相同的整数划分,不同点是,不需要在操作二给全部数加
1
1
,因为可以相同,但是此时加入到的数不是,而是
n−−√+1
n
+
1
。
最后合并两个dp结果。
第一个dp,时间复杂度 O(nn−−√) O ( n n ) ,空间复杂度 O(n) O ( n ) 。第二个dp,时间复杂度 O(nn−−√) O ( n n ) ,空间复杂度 O(n) O ( n ) 。合并时间复杂度 O(n) O ( n ) 。每次查询复杂度 O(n) O ( n ) ,使用五边形数可以搞到 O(1) O ( 1 ) 。
本题属于相等整数划分,直接分段dp求解。注意由于求出来的数是指数,且模数是指数,在dp过程中应该模 (mod−1) ( m o d − 1 ) 。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=2e5+7;
const LL mod=999999599;
using namespace std;
LL n,m,k,ans;
LL f[maxn],g[2][maxn],sum[maxn];
LL power(LL x,LL y)
{
if (y==1) return x;
LL c=power(x,y/2);
c=(c*c)%mod;
if (y%2) c=(c*x)%mod;
return c;
}
int main()
{
scanf("%lld%lld",&n,&m);
k=trunc(sqrt(n))+1;
f[0]=1;
for (LL i=1;i<=k;i++)
{
for (LL j=i;j<=n;j++)
{
f[j]=(f[j]+f[j-i])%(mod-1);
}
}
g[0][0]=1;
sum[0]=1;
int p=1;
for (LL i=1;i<=k;i++)
{
memset(g[p],0,sizeof(g[p]));
for (LL j=1;j<=n;j++)
{
if (j-k-1>=0) g[p][j]=(g[p][j]+g[p^1][j-k-1])%(mod-1);
if (j-i>=0) g[p][j]=(g[p][j]+g[p][j-i])%(mod-1);
sum[j]=(sum[j]+g[p][j])%(mod-1);
}
p^=1;
}
for (int j=0;j<=n;j++) ans=(ans+f[j]*sum[n-j]%(mod-1))%(mod-1);
printf("%lld",power(m,ans));
}