Codeforces Round 919 (Div. 2) E. Counting Binary Strings

E. Counting Binary Strings

E

题意

定义一个字串 s s s g o o d good good 当且将当: s s s 有且仅有 1 1 1 个字符 ′ 1 ′ '1' 1
请统计有多少个字符串:恰好 n n n g o o d good good 的字串,且每个 g o o d good good 的字串长度都不大于 k k k

思路

先从贡献的角度考虑一个串 s s s 有多少个 g o o d good good 的字串, 从官方题解的例子来看:每一个 1 1 1 的贡献都是其两边的 0 0 0 的数量 + 1 +1 +1 的乘积, + 1 +1 +1 是因为 1 1 1 本身也要算进去。

1
那么上面这个串的 g o o d good good 字串数量就是: a 1 a 2 + a 2 a 3 + a 3 a 4 + a 4 a 5 a_1 a_2 + a_2 a_3 + a_3 a_4 + a_4 a_5 a1a2+a2a3+a3a4+a4a5
可以发现总的数量就是数组 a a a 中相邻两个元素的乘积,并且我们如果在末尾加上一个新的元素 a j a_j aj ,是不会影响前面的结果的,而且可以直接使用前面的结果来计算新的数量。那么我们可以考虑 D P DP DP
d p i , j dp_{i,j} dpi,j 表示恰好为 i i i ,且 a a a 的最后一个元素是 j j j 的字符串数量。那么我们可以写出转移方程:

d p [ i ] [ j ] = ∑ p = 1 m i n ( ⌊ i j ⌋ , k − j + 1 ) d p [ i − p ⋅ j ] [ p ] dp[i][j] = \sum_{p=1} ^ {min(\lfloor \frac{i}{j} \rfloor , k-j+1)} dp[i-p \cdot j][p] dp[i][j]=p=1min(⌊ji,kj+1)dp[ipj][p]

意思就是枚举最后一位为 j j j,再枚举倒数第二位为 p p p,注意 p ⋅ j ≤ i p \cdot j \leq i pji p + j − 1 ≤ k p + j -1 \leq k p+j1k g o o d good good串长度不能超过 k k k

初始化要令 ∀ j ∈ [ 1 , k ] , d p [ 0 ] [ j ] = 1 \forall j \in [1,k] ,dp[0][j] = 1 j[1,k]dp[0][j]=1,这是由于如果后面某一个和 i i i,其最后两个元素乘起来刚好是 i i i 的情况,也就是对应 a a a 长度只有 2 2 2,即 s = 00000001 s = 00000001 s=00000001 s = 100000 s = 100000 s=100000 这种情况。

这样子对于每一个 i i i j j j,我们枚举 p p p 最多到 ⌊ i j ⌋ \lfloor \dfrac{i}{j} \rfloor ji ,所有 j j j 求和起来就是 ⌊ i 1 ⌋ + ⌊ i 2 ⌋ + . . . + ⌊ i i ⌋ = O ( i log ⁡ i ) \lfloor \frac{i}{1} \rfloor + \lfloor \frac{i}{2} \rfloor +...+ \lfloor \frac{i}{i} \rfloor = O(i \log i) 1i+2i+...+ii=O(ilogi)
最终的时间复杂度就是 O ( n ∑ i log ⁡ i ) = O ( n 2 log ⁡ n ) O(n \sum i \log i) = O(n ^2 \log n) O(nilogi)=O(n2logn)

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3e;
const long long INFLL=1e18;

typedef long long ll;

template<class T>
constexpr T power(T a, ll b){
    T res = 1;
    while(b){
        if(b&1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
    ll res = a * b - ll(1.L * a * b / mod) * mod;
    res %= mod;
    if(res < 0) res += mod; //误差
    return res;
}

template<ll P>
struct MLL{
    ll x;
    constexpr MLL() = default;
    constexpr MLL(ll x) : x(norm(x % getMod())) {}

    static ll Mod;
    constexpr static ll getMod(){
       if(P > 0) return P;
       return Mod;
    }

    constexpr static void setMod(int _Mod){
       Mod = _Mod;
    }
    constexpr ll norm(ll x) const{
       if(x < 0){
           x += getMod();
       }
       if(x >= getMod()){
           x -= getMod();
       }
       return x;
    }
    constexpr ll val() const{
       return x;
    }
    explicit constexpr operator ll() const{ 
       return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
    }
    constexpr MLL operator -() const{ //负号,等价于加上Mod
       MLL res;
       res.x = norm(getMod() - x);
       return res;
    }
    constexpr MLL inv() const{
       assert(x != 0);
       return power(*this, getMod() - 2); //用费马小定理求逆
    }
    constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
       x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
       return *this;
    }
    constexpr MLL& operator += (MLL rhs) & {
       x = norm(x + rhs.x);
       return *this;
    }
    constexpr MLL& operator -= (MLL rhs) & {
       x = norm(x - rhs.x);
       return *this;
    }
    constexpr MLL& operator /= (MLL rhs) & {
       return *this *= rhs.inv();
    }
    friend constexpr MLL operator * (MLL lhs, MLL rhs){
       MLL res = lhs;
       res *= rhs;
       return res;
    }
    friend constexpr MLL operator + (MLL lhs, MLL rhs){
       MLL res = lhs;
       res += rhs;
       return res;
    }
    friend constexpr MLL operator - (MLL lhs, MLL rhs){
       MLL res = lhs;
       res -= rhs;
       return res;
    }
    friend constexpr MLL operator / (MLL lhs, MLL rhs){
       MLL res = lhs;
       res /= rhs;
       return res;
    }
    friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
       ll v;
       is >> v;
       a = MLL(v);
       return is;
    }
    friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
       return os << a.val();
    }
    friend constexpr bool operator == (MLL lhs, MLL rhs){
       return lhs.val() == rhs.val();
    }
    friend constexpr bool operator != (MLL lhs, MLL rhs){
       return lhs.val() != rhs.val();
    }
};

const ll mod = 998244353;
using Z = MLL<mod>;

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int t;
    std::cin>>t;
    while(t--){
        int n,k;
        std::cin>>n>>k;
        std::vector<std::vector<Z>> dp(n+1, std::vector<Z>(k+1, 0));
        fore(i,1,k+1) dp[0][i] = 1;
        Z ans = 0;
        fore(i,1,n+1){
            fore(j,1,k+1)
                fore(p,1,std::min(i/j, k-j+1) + 1)
                    dp[i][j] += dp[i - p*j][p];
        }
        fore(j,1,k+1) ans += dp[n][j];
        std::cout<<ans<<endl;
    }
    return 0;
}
  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值