Codeforces 660E Different Subsets For All Tuples【组合数学】

看了官方题解+q神的讲解才懂。。。
智商问题。。
讲道理。。数学真的比脱单难啊。。。

题目链接:

http://codeforces.com/problemset/problem/660/E

题意:

给定数字范围,问由这些数字组成的长度为n的串的子序列有多少种?

分析:

方法一:

枚举长度k,计算以其为子序列的原串种数。
k=0 时, ans=mn
k1 时,设序列元素为 x1,x2,x3...xk ,为了避免重复,我们假设当前位置是第一次出现 xi ,即要求 x1 之前的元素不会出现 x1 x1 x2 之间的元素不会出现 x2 ,依次类推,每个间隔中的每个元素都为 m1 种。
最后枚举 xk 结束的位置,即 xk 后面的元素个数 j ,这些元素每个有 m 种可能。由于我们枚举了 xk 的位置,那么前 k1 个元素的位置就有 (nj1k1) 种。
这样对于每个 k ,我们可以得到公式:

j=0nkmkmj(m1)njk(nj1k1)

s=nj ,则有
k=1ns=knmkmns(m1)sk(s1k1)

=s=1nk=1smkmns(m1)sk(s1k1)

后一项求和利用二项式定理进行化简,最终得到

=s=1nmns+1(2m1)s1

代码:

#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
int quick_pow(int a, int b)
{
    int ans = 1;
    for(;b;b >>= 1, a = a * 1ll * a % mod){
        if(b & 1) ans = ans * 1ll * a % mod;
    }
    return ans;
}
int main()
{
    int n, m;cin>>n>>m;
    int ans = quick_pow(m, n);
    for(int s = 1; s <= n; s ++)
        ans = (ans + quick_pow(m, n - s+ 1) * 1ll * quick_pow( 2 * m - 1, s - 1)) % mod;
    cout<<ans<<endl;
    return 0;
}

然后可以发现这个就是个等比数列。
最后不要忘记加上空串的 mn

#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
int quick_pow(int a, int b)
{
    int ans = 1;
    for(;b;b >>= 1, a = a * 1ll * a % mod){
        if(b & 1) ans = ans * 1ll * a % mod;
    }
    return ans;
}
int main()
{
    int n, m;cin>>n>>m;
    int ans ;
    if(m == 1) ans = n + 1;
    else
    ans = (quick_pow(m, n) + m * 1ll * (quick_pow(2 * m - 1, n) - quick_pow(m, n)) % mod * quick_pow(m - 1, mod - 2) % mod) % mod;
    cout<<ans<<endl;
    return 0;
}

方法二:

还有一种卿学姐的dp方法。。

你可能感兴趣的:(Codeforces 660E Different Subsets For All Tuples【组合数学】)