UPC(1.14)两道dp(易超时+取模)

dp题中经常因为数值过大而要求取模,往往是取模于1e9+7。
1e9+7这个数很特殊,它是一个质数,也常常出现。所以我这里讲一下dp题(不止这种题)中出现1e9+7的小技巧。

1、设dp数组时,尽量用int,如 long long dp[maxn][maxn](这里maxn较大(比如1000以上)的话,直接超时)。

2、1e9+7是一个质数,所以用逆元的知识的话,如果要求一个数除以2的k次方(k很大)的时候。往往是用下面的公式。

using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll qpower(ll a, int b){		//快速幂
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}

用
qpower(qpower(2,k),mod-2)*dp[n][m]%mod
代替
(dp[n][m]/(qpower(2,k)))%mod

3、取模后,dp的状态转移方程往往要加点小修改。

dp[i][j]=(int)(2ll*dp[i-1][j-1]%mod+dp[i-1][j+1])%mod;

将公式改成long long的类型运算,最后外面套一个强制类型转换。这样可以避免数据溢出。
但有时这还不够,如果遇到减号,最后可能会得出负数结果,所以取模内部可以加mod。

temp=(F[b[i]][k]-F[max(a[i]-1,0)][k]+mod)%mod;

4、不出意外的话,数组得开的比较大时,仅仅dp的递推运算复杂度就很高了,如果还要在递推过程中做其他比较大的运算,99%可能性超时,所以其他运算往往要用一个备忘录来记录(俗称:打表)。

下面是两道例题(中等难度)

第一题可以用遍历检验一下,看看第41行+mod去掉之后的区别。当然也可以改改其它的,看看上述的小技巧是否有用。第二题亦是如此。

Children and Candies II

There are N children in AtCoder Kindergarten, conveniently numbered 1 through N. Mr. Evi will distribute C indistinguishable candies to the children.
If child i is given a candies, the child’s happiness will become xia, where xi is the child’s excitement level. The activity level of the kindergarten is the product of the happiness of all the N children.
For each possible way to distribute C candies to the children by giving zero or more candies to each child, calculate the activity level of the kindergarten. Then, calculate the sum over all possible way to distribute C candies. This sum can be seen as a function of the children’s excitement levels x1,…,xN, thus we call it f(x1,…,xN).
You are given integers Ai,Bi(1≤i≤N). Find 在这里插入图片描述modulo 10^9+7.

Constraints
1≤N≤400
1≤C≤400
1≤Ai≤Bi≤400(1≤i≤N)
Partial Score
400 points will be awarded for passing the test set satisfying Ai=Bi(1≤i≤N).
输入
The input is given from Standard Input in the following format:

N C
A1 A2 … AN
B1 B2 … BN
输出
Print the value of 在这里插入图片描述modulo 10^9+7.
样例输入 Copy
2 3
1 1
1 1
样例输出 Copy
4
提示
This case is included in the test set for the partial score, since Ai=Bi. We only have to consider the sum of the activity level of the kindergarten where the excitement level of both child 1 and child 2 are 1 (f(1,1)).

If child 1 is given 0 candy, and child 2 is given 3 candies, the activity level of the kindergarten is 1013=1.
If child 1 is given 1 candy, and child 2 is given 2 candies, the activity level of the kindergarten is 11
12=1.
If child 1 is given 2 candies, and child 2 is given 1 candy, the activity level of the kindergarten is 1211=1.
If child 1 is given 3 candies, and child 2 is given 0 candy, the activity level of the kindergarten is 13
10=1.
Thus, f(1,1)=1+1+1+1=4, and the sum over all f is also 4.

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=405;
const int mod=1e9+7;
ll qpower(ll a, int b){
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
int n,c;
int a[maxn],b[maxn];
ll dp[maxn][maxn],f[maxn][maxn],F[maxn][maxn];
void init(void){						//类似备忘录 
	f[0][0]=1;
	for(int i=1;i<maxn;i++){
		f[i][0]=1;	F[i][0]=1;
		for(int j=1;j<maxn;j++)	f[i][j]=(f[i][j-1]*i)%mod;		
	}
	for(int i=1;i<maxn;i++)
		for(int j=0;j<maxn;j++)
			F[i][j]=(F[i-1][j]+f[i][j])%mod;
}
int main(){
	cin>>n>>c;
	for(int i=1;i<=n;i++)	cin>>a[i];
	for(int i=1;i<=n;i++)	cin>>b[i];
	init();
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=c;j++){
			for(int k=0;k<=j;k++){
				ll temp=0;
//				for(int x=a[i];x<=b[i];x++)	temp=(temp+qpower(x,k))%mod;		//直接求k^a[i]+...+k^b[i]=>超时 
				temp=(F[b[i]][k]-F[max(a[i]-1,0)][k]+mod)%mod;
				dp[i][j]=(dp[i][j]+(dp[i-1][j-k]*temp)%mod)%mod;
			}
		}
	}
	
//	for(int i=1;i<=n;i++){				//遍历检验 
//		for(int j=0;j<=c;j++)
//			cout<<dp[i][j]<<' ';
//		cout<<endl;
//	}
	cout<<dp[n][c];
}

Unhappy Hacking II

题目描述
Sig has built his own keyboard. Designed for ultimate simplicity, this keyboard only has 3 keys on it: the 0 key, the 1 key and the backspace key.
To begin with, he is using a plain text editor with this keyboard. This editor always displays one string (possibly empty). Just after the editor is launched, this string is empty. When each key on the keyboard is pressed, the following changes occur to the string:
The 0 key: a letter 0 will be inserted to the right of the string.
The 1 key: a letter 1 will be inserted to the right of the string.
The backspace key: if the string is empty, nothing happens. Otherwise, the rightmost letter of the string is deleted.
Sig has launched the editor, and pressed these keys N times in total. As a result, the editor displays a string s. Find the number of such ways to press the keys, modulo 10^9+7.

Constraints
1≤N≤5000
1≤|s|≤N
s consists of the letters 0 and 1.
Partial Score
400 points will be awarded for passing the test set satisfying 1≤N≤300.

输入
The input is given from Standard Input in the following format:
N
s
输出
Print the number of the ways to press the keys N times in total such that the editor displays the string s in the end, modulo 10^9+7.
样例输入 Copy
3
0
样例输出 Copy
5
提示
We will denote the backspace key by B. The following 5 ways to press the keys will cause the editor to display the string 0 in the end: 00B, 01B, 0B0, 1B0, BB0. In the last way, nothing will happen when the backspace key is pressed.

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=5005;
const int mod=1e9+7;
ll qpower(ll a, int b){
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}

string a;
int n;
int dp[maxn][maxn],sum;
int main(){
	cin>>n;
	getchar();
	getline(cin,a);
	int len=a.size();
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i;j++){
			if(j==0)	dp[i][0]=(int)(dp[i-1][j+1]+dp[i-1][0])%mod;
			else	dp[i][j]=(int)(2*dp[i-1][j-1]%mod+dp[i-1][j+1])%mod;
		}
	}
//	下面这种写法也可以 
//	for(int i=0;i<=n;i++){
//        for(int j=0;j<=i;j++){
//            dp[i+1][j+1]=(int)(( 2ll*dp[i][j]%mod + dp[i+1][j+1] )%mod);
//            dp[i+1][max(j-1,0)]=(int)(( 0ll+dp[i][j]+dp[i+1][max(j-1,0)])%mod);
//        }
//    }
//	遍历检验 
//	for(int i=1;i<=n;i++){
//		for(int j=0;j<=i;j++)
//			cout<<dp[i][j]<<' ';
//		cout<<endl;
//	}
	sum=(int)(qpower(qpower(2,len),mod-2)*dp[n][len]%mod);
	cout<<sum;
}

猜你喜欢

转载自blog.csdn.net/weixin_45606191/article/details/104056497