Add or Multiply 1
题意:
- 让求方案数
分析:
-
模几组小数据,发现连续出现一段乘或加并不会对答案造成影响,并且发现乘和加是一个道理
换一种说法,从整个序列来看,最后肯定是一段区间乘,一段区间加,一段…是相间隔的
-
现在对 n n n 进行操作,将 n n n 分为 k 1 k_1 k1 组(非空组,下同), k 1 ∈ 1 , 2 , 3... n k_1\in1,2,3...n k1∈1,2,3...n , m m m 分为 k 2 组 k_2组 k2组 , k 2 ∈ 1 , 2 , 3... m k_2\in1,2,3...m k2∈1,2,3...m
然后就是先放 k 1 k_1 k1 个 + + + ,再插空放 ∗ * ∗ ,有三种情况:
-
有 k 1 − 1 k_1-1 k1−1 组 ∗ * ∗ , + + + 把 ∗ * ∗ 包起来
-
有 k 1 k_1 k1 组 ∗ * ∗ ,先 + + + 或先 ∗ * ∗ , a n s ans ans 要乘 2 2 2
-
有 k 1 + 1 k_1+1 k1+1 组 ∗ * ∗ , ∗ * ∗ 把 + + + 包起来
所以现在要做的就是,打表将 n n n 分为 k 1 组 k_1组 k1组 , k 1 ∈ 1 , 2 , 3... n k_1\in1,2,3...n k1∈1,2,3...n ,的方案数
下面看一个定义:
直接套板子了…
-
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mo=1e9+7, N=3005;
int a[N], s[N][N];
void init()
{
a[0]=1;
for(int i=1;i<=3000;i++) a[i] = a[i-1]*i%mo;
s[1][1]=1; // 斯特林数
for(int i=2;i<=3000;i++)
{
for(int j=1;j<=i;j++)
{
s[i][j] = (s[i-1][j-1]+j*s[i-1][j])%mo;
}
}
for(int i=1;i<=3000;i++)
{
for(int j=1;j<=i;j++)
{
s[i][j] = s[i][j]*a[j]%mo;
// j 段还要进行一遍全排列
}
}
}
signed main()
{
init();
int T;
cin>>T;
while(T--)
{
int n,m,ans=0;
cin>>n>>m;
if(n>m) swap(n,m); // 乘和加是一个道理,可以优化一下
for(int i=1;i<=n;i++)
{
ans = (ans+s[n][i]*s[m][i-1])%mo;
ans = (ans+2*s[n][i]*s[m][i])%mo;
if(m>=i+1) ans = (ans+s[n][i]*s[m][i+1])%mo;
}
cout<<ans<<endl;
}
return 0;
}