DP题型

真题

[CSP-S 2021] 括号序列

[NOIP2021] 数列

[NOIP2021] 方差

[NOIP2023] 天天爱打卡

典题

1.ρars/ey

书上背包

image

2.CF1515E Phoenix and Computers

记录状态为f[i][j]表示前i个数,手动开启了j个

image

转移方程为:

f[i+l+1][j+l]=f[i][j]2^(l-1)C(j,l+j)

3.CF1572C Paint

区间DP

发现将一个区间按照开头/结尾染色一定较优(手玩样例)

image

(每种颜色的数量不超过20,故常数省略)

4.image

区间DP

然后限制变成二维数点

image

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子树最大奇/偶

转移易做

image

6.CF1801F Another n-dimensional chocolate bar

前置知识:

数论分块,O(sqrt(N))(会跑满,N小的时候约为2*sqrt(N))

因子个数,O(image

image

7.CF1647F Madoka and Laziness

发现最大值一定是某个峰

将序列划分成3部分

每部分单独DP

image

在[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;
}
posted @ 2025-07-29 22:39  gbrrain  阅读(4)  评论(0)    收藏  举报