[CSP-S模拟测试]:花(DP)

题目传送门(内部题111)


输入格式

  一个整数$T$,表示测试数据组数。
  每组测试数据占一行,两个整数,分别表示$L$和$S$。


输出格式

  对每组数据,输出一个整数表示答案。


样例

样例输入1:

1
3 7

样例输出1:

7

样例输入2:

2
4 2
10 11

样例输出2:

4
410199993


数据范围与提示

样例$1$解释:

  一共有$7$种形态,每种形态能构成$1$个方案。

样例$2$解释:

  AAAB
  ABBB
  BAAA
  BBBA

数据范围:

  对于$60\%$的数据,$L\leqslant 30,S\leqslant 26$。
  对于$80\%$的数据,$L\leqslant 10,000,S\leqslant 26$。
  对于$100\%$的数据,$L\leqslant 100,000,S\leqslant 100,000$。


题解

不给暴力分,导致考场许多想偏了的$dalao$直播$TLE0$……

考虑$DP$。

先来考虑$80$分的做法。

因为在处理当前位的时候只与最后两位有关系,于是我们可以设$dp[i][j][k][0/1]$表示处理到$i$,当前位是$k$,上一位为$j$,是否已经出现过三连的方案数。

转移很暴力,枚举$j,k$,再枚举当前位是什么就好了。

发现形态只有相同与不同,而与具体是哪种情况无关,于是考虑换一个状态定义,说一下我的做法,与题解略有不同,设$dp[i][0/1][0/1]$表示处理到$i$,是否与上一位相同,是否出现过三连即可。

下面给出四个状态转移方程:

  $\alpha.dp[i][0][0]=(dp[i-1][0][0]+dp[i-1][1][0])\times (S-1)$:可以由上一位二连或不是二连转移过来,只要与上一位不同即可,所以要乘上$(S-1)$;但是不能三连边没有。

  $\beta.dp[i][0][1]=(dp[i-1][0][1]+dp[i-1][1][1])\times (S-1)$:不能创造三连,只能继承上面的三连,$(S-1)$与上式同理。

  $\gamma.dp[i][1][0]=dp[i-1][0][0]$:只能由上一位没有二连转移过来,否则会出现三连,那么第三维就不能是$0$了。

  $\delta.dp[i][1][1]=dp[i-1][0][1]+dp[i-1][1][0]$:这次可以继承前面的三连,但是注意不能再出现三连;还可以创造三连,这时候需要上一位是二连。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int L,S;
long long dp[100001][2][2];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&L,&S);
		memset(dp,0,sizeof(dp));
		dp[2][0][0]=1LL*S*(S-1)%mod;
		dp[2][1][0]=S;
		for(int i=3;i<=L;i++)
		{
			dp[i][0][0]=(dp[i-1][0][0]+dp[i-1][1][0])%mod*(S-1)%mod;
			dp[i][0][1]=(dp[i-1][0][1]+dp[i-1][1][1])%mod*(S-1)%mod;
			dp[i][1][0]=dp[i-1][0][0];
			dp[i][1][1]=(dp[i-1][0][1]+dp[i-1][1][0])%mod;
		}
		printf("%lld\n",(dp[L][0][1]+dp[L][1][1])%mod);
	}
	return 0;
}

rp++

猜你喜欢

转载自www.cnblogs.com/wzc521/p/11779145.html