DP题型
真题
[CSP-S 2021] 括号序列
[NOIP2021] 数列
[NOIP2021] 方差
[NOIP2023] 天天爱打卡
典题
1.ρars/ey
书上背包

2.CF1515E Phoenix and Computers
记录状态为f[i][j]表示前i个数,手动开启了j个

转移方程为:
f[i+l+1][j+l]=f[i][j]2^(l-1)C(j,l+j)
3.CF1572C Paint
区间DP
发现将一个区间按照开头/结尾染色一定较优(手玩样例)

(每种颜色的数量不超过20,故常数省略)
4.![image]()
区间DP
然后限制变成二维数点

5.![image]()
有一个性质
当u子树里能凑出K,而u的所有儿子都凑不出K,直接用u这整个子树凑一个,并把这个子树删除
现在要判断:某个子树能否凑出K
发现如果设集合总和为S,若S-1能凑出来,S-3,S-5……都能拼出来
同理,若S-2能凑出来,S-4,S-6……也都能拼出来
故不用背包
只需知道每个联通块能拼的最大奇数与最大偶数
则小于它们的奇数/偶数都能拼了
设f[i][0/1]表示i子树最大奇/偶
转移易做

6.CF1801F Another n-dimensional chocolate bar
前置知识:
数论分块,O(sqrt(N))(会跑满,N小的时候约为2*sqrt(N))
因子个数,O(
)

7.CF1647F Madoka and Laziness
发现最大值一定是某个峰
将序列划分成3部分
每部分单独DP

在[1,maxP]区间里的DP形如:
dp[i]表示一个序列以i结尾,另一个序列结尾最小是多少
if(a[i]>a[i-1]) dp[i]=dp[i-1];//a[i]放在与a[i-1]同一序列中,另一个序列的结尾不变
if(a[i]>dp[i-1]) dp[i]=a[i-1];//a[i]放在与a[i-1]不同序列中,另一个序列的结尾即为a[i-1]
若都满足条件,二者取min
其余两个部分类似
8.CF908E New Year and Entity Enumeration
首先有一个附加性质是a or b一定在集合S中
观察样例发现
将原T集合按位分组
将几位变化永远相同的数分为一组
最少的集合数量为2^组数
然后再对每个集合进行划分
这个是简单DP
dpn表示集合大小为n的方案
发现每个集合之间互相独立
计算答案乘法原理
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
const int MOD=1e9+7;
int ksm(int a,int b,int p){
if(b==0) return 1;
if(b==1) return a%p;
int c=ksm(a,b/2,p);
c=c*c%p;
if(b%2==1) c=c*a%p;
return c%p;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int N=1000;
int fac[1006],infac[1006],inv[1006],dp[1005],val[1005];
map<int,int> mm;
int C(int n,int m){
return fac[n]*infac[m]%MOD*infac[n-m]%MOD;
}
signed main()
{
//freopen("filename.in", "r", stdin);
//freopen("filename.out", "w", stdout);
fac[0]=1;
for(int i=1;i<=N;i++){
fac[i]=fac[i-1]*i%MOD;
fac[i]%=MOD;
}
infac[N]=ksm(fac[N],MOD-2,MOD);
for(int i=N-1;i>=0;i--){
infac[i]=infac[i+1]*(i+1)%MOD;
}
dp[0]=1;
for(int i=0;i<=1000;i++){
for(int j=1;j<=1000;j++){
if(i+j>1000) break;
jiaa(dp[i+j],dp[i]*C(i+j-1,j-1)%MOD);
}
}
int m=read(),n=read();
for(int i=1;i<=n;i++){
string s;
cin>>s;
for(int j=0;j<m;j++){
if(s[j]=='1') val[j]+=(1<<(i-1));
}
}
for(int i=0;i<m;i++) mm[val[i]]++;
int ans=1;
for(int i=0;i<m;i++){
ans=ans*dp[mm[val[i]]]%MOD;
mm[val[i]]=0;
}
cout<<ans<<'\n';
return 0;
}



浙公网安备 33010602011771号