计蒜客NOIP模拟赛(2) D2T2紫色百合
【问题描述】
“牵着你的手的是她,路边开满了紫色的百合花……”
你从梦中醒来,却依然忘不了梦中的她百合花,每朵百合花都有一个权值,在二进制下写成一行‘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))
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long lol; 7 lol Mod=998244353; 8 int f[451][100011]; 9 lol ans; 10 lol n,p; 11 int main() 12 {lol i,j; 13 cin>>n>>p; 14 f[0][0]=1; 15 for (i=1;i*(i+1)/2<=p;i++) 16 { 17 for (j=i;j<=p;j++) 18 { 19 f[i][j]=f[i-1][j-i]+f[i][j-i]; 20 if (j>=(n+1)) f[i][j]-=f[i-1][j-n-1]; 21 if (f[i][j]<0) f[i][j]+=Mod; 22 if (f[i][j]>=Mod) f[i][j]-=Mod; 23 } 24 ans=(ans+f[i][p])%Mod; 25 } 26 cout<<ans%Mod; 27 }
【问题描述】
“牵着你的手的是她,路边开满了紫色的百合花……”
你从梦中醒来,却依然忘不了梦中的她百合花,每朵百合花都有一个权值,在二进制下写成一行‘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 |