CodeForces 696D AC自动机+DP+矩阵快速幂

题意

给一组字符串,每个字符串有一个值,如果一个字符串包含这个子串,那么总值就加上这个子串的值。给一个长度,问能组成的最大值。

题解

对于要求长度1000的情况,我们可以直接DP。但是这道题不
行,这道题长度达到了10^16。我们考虑用矩阵快速幂加速。
我们很容易便能得到一个状态转移方程,dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j])。这个状态转移方程中的I,J,K都是AC自动机中的点。这个看起来像区间DP的状态转移方程其实有着很有趣的特性。我们可以发现这个状态转移4次的值,实际上就是转移两次的矩阵+转移两次的矩阵(这里的加法进行了重新定义,计算方式就是上述那个状态转移方程)。有了这个性质,我们便可以直接利用矩阵快速幂进行计算了。最后的话,取从0开始的最大值就可以了。

注意事项

需要注意进行矩阵快速幂的时候初始化矩阵不能新建一个矩阵,而是要利用计算好的A矩阵。因为如果新建一个矩阵就意味着可以从任意一个点开始,这样的话会造成结果的偏差。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 220
#define MOD 1000000007
#define EPS 1e-3
#define int LL
using namespace std;
int ch[MAXN][26];
int val[MAXN],aval[220],f[MAXN];
char s[220];
int sz;

struct Matrix{
    int mt[MAXN][MAXN];
    void init(){
        MEM(mt,0);
    }
    Matrix operator + (const Matrix b) const{
        Matrix m;
        m.init();
        UP(i,0,sz){
            UP(j,0,sz){
                m.mt[i][j]=-INF;
                UP(k,0,sz){
                    m.mt[i][j]=max(m.mt[i][j],mt[i][k]+b.mt[k][j]);
//                    if(m.mt[i][j]==8) cout<<m.mt[i][j]<<" "<<i<<" "<<j<<" "<<k<<endl;
                }
            }
        }
        return m;
    }

};

Matrix quick_pow(Matrix a,int n){
    Matrix m=a;
    n--;
    W(n){
        if(n&1) m=m+a;
        a=a+a;
        n>>=1;
    }
    return m;
}

void insert(int k){
    int u=0,n=strlen(s);
    UP(i,0,n){
        int c=s[i]-'a';
        if(!ch[u][c]){
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    val[u]+=aval[k];
//    cout<<u<<" "<<val[u]<<endl;
}

void getFail(){
    queue<int> q;
    MEM(f,0);
    UP(c,0,26){
        if(ch[0][c]){
            q.push(ch[0][c]);
        }
    }
    W(!q.empty()){
        int r=q.front();
        q.pop();
        UP(c,0,26){
            int u=ch[r][c];
            if(!u){ch[r][c]=ch[f[r]][c];continue;}
            q.push(u);
            int v=f[r];
            f[u]=ch[v][c];
            val[u]+=val[f[u]];
        }
    }
}

main(){
    MEM(val,0);
    MEM(ch,0);
    sz=1;
    int n,l;
    scanf("%I64d%I64d",&n,&l);
    UP(i,0,n) scanf("%I64d",&aval[i]);
    UP(i,0,n) scanf("%s",s),insert(i);
    getFail();
    Matrix mt;
    UP(i,0,sz) UP(j,0,sz) mt.mt[i][j]=-INF;
    UP(i,0,sz){
        UP(j,0,26){
            if(ch[i][j]) mt.mt[i][ch[i][j]]=val[ch[i][j]];
        }
    }
    Matrix ans=quick_pow(mt,l);
    int mx=0;
    UP(i,0,sz){
        mx=max(mx,ans.mt[0][i]);
//        cout<<ans.mt[0][i]<<" "<<i<<endl;
    }
    printf("%I64d\n",mx);
}
### Codeforces 平台上的快速幂算法题目及相关实现 #### 快速幂算法简介 快速幂是一种高效的计算 $a^b \mod c$ 的算法,其时间复杂度为 $O(\log b)$。通过将指数分解成二进制形式并利用平方倍增的方式减少乘法次数来提高效率。 --- #### 题目解析与实现方法 以下是两道来自 Codeforces 平台上涉及快速幂的经典题目及其解决方案: --- ##### **题目一:Codeforces 1594B** 这是一道典型的快速幂应用题,目标是找到第 $k$ 个以 $n$ 为底的幂底数累加的结果[^1]。 ###### 解决方案 给定整数 $n$ 和 $k$,我们需要按照 $k$ 的二进制表示逐位判断哪些位置对应的幂需要被加入最终结果中。具体实现如下: ```cpp #include <iostream> #define ll long long using namespace std; const int MOD = 1e9 + 7; int main() { int t; cin >> t; while (t--) { ll n, k; cin >> n >> k; ll ans = 0, s = 1; // 初始化答案和当前幂值 while (k != 0) { if (k & 1) { // 如果当前位为1,则加上对应幂值 ans = (ans + s) % MOD; } s = s * n % MOD; // 更新幂值 k >>= 1; // 右移一位 } cout << ans << endl; } return 0; } ``` 此代码的核心在于使用 `while` 循环逐步处理 $k$ 的每一位,并根据是否为 $1$ 来决定是否将其对应的幂值加入到总和中。 --- ##### **题目二:CodeForces - 894B Ralph And His Magic Field** 本题同样涉及到快速幂的应用,但更侧重于组合数学中的计数问题[^2]。 ###### 解决方案 对于输入的三个参数 $n$, $m$, 和 $k$,我们可以通过快速幂函数高效地求解 $(2^{(n-1)})^{(m-1)} \mod INF$ 的值。如果 $k=-1$ 且 $(n+m)\%2\neq0$,则直接返回 $0$;否则继续执行快速幂逻辑。 ```cpp #include <iostream> using namespace std; const long long INF = 1e9 + 7; long long fast_pow(long long base, long long exp) { long long result = 1; base %= INF; while (exp > 0) { if (exp & 1) { result = (result * base) % INF; } base = (base * base) % INF; exp >>= 1; } return result; } int main() { long long n, m, k; cin >> n >> m >> k; if (k == -1 && (n + m) % 2 != 0) { cout << "0" << endl; return 0; } cout << fast_pow(fast_pow(2, n - 1), m - 1) << endl; return 0; } ``` 这段程序定义了一个通用的快速幂辅助函数 `fast_pow`,用于简化主流程中的幂运算部分[^2]。 --- #### 总结 以上两个例子展示了如何在不同场景下灵活运用快速幂技术解决问题。无论是简单的累加还是复杂的嵌套幂运算,都可以借助这一技巧显著提升性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值