【问题描述】

“牵着你的手的是她,路边开满了紫色的百合花……”

你从梦中醒来,却依然忘不了梦中的她百合花,每朵百合花都有一个权值,在二进制下写成一行‘1’,第i朵紫色百合的权值在二进制下写成i个‘1’。你想挑出其中一些组成“一束百合花”且价值在二进制下恰好为一个‘1’后面P个‘0’,那么有多少种挑选方案呢?

定义“一束百合花”的价值为这些百合花组成的集合的所有子集的权值乘积的和(空集的权值乘积算1)。如价值为1和3组成的一束百合花价值为1+1+3+1*3=8

【输入格式】

一行两个正整数N,P,含义如题目中所示n,p<=100000

【输出格式】

一个整数代表方案数模 998244353 的结果

【样例输入1

3 3

【样例输出1

2

【样例输入2

233 666

【样例输出2

572514965

稍稍运用一下数学知识发现题目要求的是选出的集合每个元素+1之后的乘积等于2^P的方案数,取个log就变成了↓

在1~N选若干个数使得总和等于P,求方案数

然后用普通的背包DP可以就拿到60分了

 然后我们发现,由于物品大小是1~N,所以最多选取O(sqrt(P))个物品,背包就满了

满分做法可以用状态f[i][j]表示选i个物品,占容量为j的方案数

由于每个背包是不同的,所以根据已选的最小的物品分类讨论一下:

如果最小的物品是1,相当于i-1个物品凑出了j-i的大小,然后整体+1

如果最小的物品不是1,相当于i个物品凑出了j-i的大小,然后整体+1

需要注意我们要防止出现选择了大小为N+1的物品的情况,所以需要减去

得到递推式f[i][j]=f[i-1][j-i]+f[i][j-i]-f[i-1][j-(N+1)]

时间复杂度O(Nsqrt(N))

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long lol;
lol Mod=;
int f[][];
lol ans;
lol n,p;
int main()
{lol i,j;
cin>>n>>p;
f[][]=;
for (i=;i*(i+)/<=p;i++)
{
for (j=i;j<=p;j++)
{
f[i][j]=f[i-][j-i]+f[i][j-i];
if (j>=(n+)) f[i][j]-=f[i-][j-n-];
if (f[i][j]<) f[i][j]+=Mod;
if (f[i][j]>=Mod) f[i][j]-=Mod;
}
ans=(ans+f[i][p])%Mod;
}
cout<<ans%Mod;
}

【问题描述】

“牵着你的手的是她,路边开满了紫色的百合花……”

你从梦中醒来,却依然忘不了梦中的她百合花,每朵百合花都有一个权值,在二进制下写成一行‘1’,第i朵紫色百合的权值在二进制下写成i个‘1’。你想挑出其中一些组成“一束百合花”且价值在二进制下恰好为一个‘1’后面P个‘0’,那么有多少种挑选方案呢?

定义“一束百合花”的价值为这些百合花组成的集合的所有子集的权值乘积的和(空集的权值乘积算1)。如价值为1和3组成的一束百合花价值为1+1+3+1*3=8

【输入格式】

一行两个正整数N,P,含义如题目中所示

【输出格式】

一个整数代表方案数模 998244353 的结果

【样例输入1

3 3

【样例输出1

2

【样例输入2

233 666

【样例输出2

572514965

【数据范围与约定】

测试点编号

N

P

1


8


100

2


12


100

3


15


100

4


100


100

5


1000


1000

6


2000


2000

7


100000


100000

8


100000


100000

9


100000


100000

10


100000


100000