zoukankan      html  css  js  c++  java
  • CodeForces 1426F Number of Subsequences

    题意

    给定一个长度为 (n) 的串,只包含 abc 和通配符。通配符可以替换 abc 的一个。求所有得到的字符串中子序列 abc 出现的次数,对 (10^9+7) 取模。

    ( exttt{Data Range:}nleq 2 imes 10^5)

    题解

    哇哈哈哈我智商终于恢复了。

    比较套路,但其实这个东西我一开始是用类似于期望的东西来想的。

    记通配符的数量为 (m)

    考虑设 (f_{i,j}) 表示所有 (3^m) 个字符串的前 (i) 个字符中,子序列 aababc 的数量之和。

    首先当给定字符串的第 (i) 个字符为 a 的时候,有如下转移:

    [f_{i,1}=f_{i-1,1}+3^m,f_{i,2}=f_{i-1,2},f_{i,3}=f_{i-1,3} ]

    b 的时候有如下转移:

    [f_{i,1}=f_{i-1,1},f_{i,2}=f_{i-1,2}+f_{i-1,1},f_{i,3}=f_{i-1,3} ]

    c 的时候有如下转移:

    [f_{i,1}=f_{i-1,1},f_{i,2}=f_{i-1,2},f_{i,3}=f_{i-1,3}+f_{i-1,2} ]

    这三个转移都很平凡,这里不多赘述。

    接下来是为通配符的情况,需要讨论一下。

    注意到我们肯定可以将所有 (3^{m}) 个字符串中直到 (i-1) 的前缀划分为三组,每组的字符串相同。

    所以说每组字符串的中的 aababc 的数量变成了 (frac{f_{i-1,1}}{3})(frac{f_{i-1,2}}{3})(frac{f_{i-1,3}}{3})

    于是考虑将第一组的后面加一个 a,第二组加一个 b,第三组加一个 c。这样子我们就可以写出一个转移方程:

    [egin{cases}f_{i,1}=frac{f_{i-1,1}+3^m}{3}+frac{2f_{i-1,1}}{3}\f_{i,2}=frac{f_{i-1,2}+f_{i-1,1}}{3}+frac{2f_{i-1,2}}{3}\f_{i,3}=frac{f_{i-1,3}+f_{i-1,2}}{3}+frac{2f_{i-1,3}}{3}end{cases} ]

    整理一下得到以下转移:

    [f_{i,1}=f_{i-1,1}+3^{m-1},f_{i,2}=f_{i-1,2}+frac{f_{i-1,1}}{3},f_{i,3}=f_{i-1,3}+frac{f_{i-1,2}}{3} ]

    线性 DP 就没了。

    有一个加强版就是说多组询问求任意子段的答案。注意到 (f_{i}) 只与 (f_{i-1}) 有关所以可以写成一个 (4 imes 4) 的矩阵,然后用线段树维护矩阵乘积就好了。可能会算重,于是除掉一下就差不多了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef int ll;
    typedef long long int li;
    const ll MAXN=2e5+51,MOD=1e9+7,INV3=333333336;
    ll n,m,pw=1;
    char ch[MAXN];
    ll f[MAXN][3];
    inline ll read()
    {
        register ll num=0,neg=1;
        register char ch=getchar();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=getchar();
        }
        if(ch=='-')
        {
            neg=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=getchar();
        }
        return num*neg;
    }
    int main()
    {
        n=read(),scanf("%s",ch+1);
        for(register int i=1;i<=n;i++)
        {
            ch[i]=='?'?m++,pw=(li)pw*3%MOD:1;
        }
        for(register int i=1;i<=n;i++)
        {
            f[i][1]=f[i-1][1],f[i][2]=f[i-1][2],f[i][3]=f[i-1][3];
            if(ch[i]=='a')
            {
                f[i][1]=(f[i][1]+pw)%MOD;
            }
            if(ch[i]=='b')
            {
                f[i][2]=(f[i][2]+f[i-1][1])%MOD;
            }
            if(ch[i]=='c')
            {
                f[i][3]=(f[i][3]+f[i-1][2])%MOD;
            }
            if(ch[i]=='?')
            {
                f[i][1]=(f[i][1]+(li)pw*INV3%MOD)%MOD;
                f[i][2]=(f[i][2]+(li)f[i-1][1]*INV3)%MOD;
                f[i][3]=(f[i][3]+(li)f[i-1][2]*INV3)%MOD;
            }
        }
        printf("%d
    ",f[n][3]);
    }
    
  • 相关阅读:
    数据库表设计
    solr的schame.xml
    搭建solr服务器
    lucene&solr索引维护之查询
    lucene&solr索引维护之删除和修改
    lucene&solr查询索引实例
    lucene&solr入门实例
    java集合排序整理
    HahMap(jdk=1.8)源码解读
    APIO2020 游记
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13751412.html
Copyright © 2011-2022 走看看