D - Number of Multisets
之前写过一个类似表示的题(2018CCPC吉林赛区——C - Justice),也是让用12,14,18…\frac1 2 ,\frac 14,\frac1 8 \dots21,41,81…凑数,我效仿之前的思路写了个复杂度不清楚的东西(非常爆炸)最终没能过此题
记一下自己写的垃圾代码
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3110;
const int mod=998244353;
int n,k;
int f[20][N][N];
ll dfs(int u,int cnt,int now)
{ if(now+cnt>n) return 0;if(!cnt) return now==n;if(u>12) return 0;if(f[u][cnt][now]!=-1) return f[u][cnt][now];f[u][cnt][now]=0;for(int i=0;i<=cnt;i++)f[u][cnt][now]=(f[u][cnt][now]+dfs(u+1,(cnt-i)*2,now+i))%mod;return f[u][cnt][now];
}
int main()
{//IO;int T=1;//cin>>T;while(T--){memset(f,-1,sizeof f);cin>>n>>k;cout<<dfs(1,k,0)<<'\n';}return 0;
}
这题的正解时划分dp,之前有一个整数划分的题,我用的完全背包做的,一直没用划分dp做,这次遇到题目就不会了hh
大佬题解
状态表示:f(i,j)f_{(i,j)}f(i,j)当前需要选出iii个数的和时jjj的方案数
状态转移:我们把方案划分成两种情况:①至少选择一个1。②不选择1
- 对于①:f(i,j)=f(i−1,j−1)f_{(i,j)}=f_{(i-1,j-1)}f(i,j)=f(i−1,j−1)
- 对于②:不选择111,此时问题转化为:问有多少种选出 iii 个数的方案,满足 i 个数的和为 jjj 。这 iii 个数都须是 12i(i∈[1,∞))\frac{1}{2^i}(i∈[1,∞))2i1(i∈[1,∞)) 的形式。我们考虑这个子问题的限制条件与原问题的限制条件的关系。如果我们将这 iii 个数同时×2×2×2,那么子问题的限制条件就和原问题的限制条件一致了。此时问题转化为求解 f(i,2j)f_{(i,2j)}f(i,2j)
然后考虑一下边界条件记忆化搜素即可。
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3010;
const int mod=998244353;
int f[N][N];
int n,k;
int dfs(int i,int j)
{if(i<=j) return i==j;if(!j) return 0;if(f[i][j]!=-1) return f[i][j];return f[i][j]=(dfs(i-1,j-1)+dfs(i,2*j))%mod;
}
int main()
{//IO;int T=1;//cin>>T;while(T--){memset(f,-1,sizeof f);cin>>n>>k;cout<<dfs(n,k)<<'\n';}return 0;
}
总结:dp的本质时递推,如果能够设计某种表示能够有一种递推关系,那么有可能该解就可以得出答案。