[POI2010] CHO-Hamsters

给出\(n\)个字符串(\(n \leq 200\)),\(\sum|S| \leq 10^5\)。让他们接龙,其中两个单词首尾相同部分会覆盖。问包含\(m\)个串(可重复,可不都用到)的最小的龙的长度。(\(m \leq 10^9\)

解题思路

这道题本质上可以转化为一个图论的模型。我们可以将每个字符串想象成一个点,一个点到另一个点的代价就是接上去后长度增加多少。这个图是有向有环(还有自环)的。题目所要求得\(m\)个串最小龙长度,就变成了一条恰好经过\(m\)个点的最短路径(起点要算上原本的长度)。

于是现在有两个问题:一个是如何快速预处理出边权,二是如何快速求出最短路径长度。

第一个问题,开个O2暴力过了。正解是要求AC自动机的。

第二个问题是个经典问题——图上经过\(k\)条边的最短路径,解法是Floyd的矩阵快速幂优化。详细说明一下:

对于一张图,起初有一个邻接矩阵。邻接矩阵中的一项\(g_{i,j}\)可以理解为是点\(i\)到点\(j\)恰好经过一条边的最短路。而现在如果要求点\(i\)到点\(j\)恰好经过两条边的最短路,应该做\(g'_{i,j}=\min\{g_{i,k}+g_{k,j}\}\)\(g'\)是恰好经过两条边的,而如果想知道三条边的那么\(g''_{i,j}=\min\{g'_{i,k}+g_{k,j}\}\)。依次类推我们有\(g^m_{i,j}=\min\{g^{m-1}_{i,k}+g_{k,j}\}\)。这是一个变种的矩阵乘法,而幸运的是它依然满足结合律。因此我们直接对\(g\)做矩阵快速幂就可以了。

值得一提的是,在标准的矩阵快速幂中,我们将\(ans\)初始化为单位矩阵。而现在我们修改了矩阵乘法的定义,意味着单位矩阵的构造也要做出一定的改动:
\[ \begin{bmatrix} 0 & \infty & \cdots & \infty \\ \infty & 0 & \cdots & \infty \\ \vdots & \vdots & \ddots & \vdots \\ \infty & \infty & \cdots & 0 \end{bmatrix} \]即对角线为0,其余都是\(\infty\)

\(Code\)

/*DennyQi 2019*/
#include 
#include 
#include 
#include 
#include 
#include 
#define int long long
using namespace std;
const int N = 100010;
const int P = 998244353;
const int INF = 999999999999999999;
inline int mul(const int& a, const int& b){ return 1ll*a*b%P; }
inline int add(const int& a, const int& b){ return (a+b>=P)?a+b-P:a+b; }
inline int sub(const int& a, const int& b){ return (a-b<0)?a-b+P:a-b; }
inline int read(){
    int x(0),w(1); char c = getchar();
    while(c^'-' && (c<'0' || c>'9')) c = getchar();
    if(c=='-') w = -1, c = getchar();
    while(c>='0' && c<='9') x = (x<<3)+(x<<1)+c-'0', c = getchar(); 
    return x*w;
}
string s[210];
int n,m,Ans,len[210],g[210][210],tmp[210][210],ans[210][210];
inline int calc_same(string a, string b, bool tp){   
    int la = a.size(), lb = b.size();
    int flg;
    for(int l = min(la,lb)-tp; l >= 0; --l){
        flg = 1;
        for(int j = 0; j < l; ++j){
            if(a[j] != b[lb-l+j]){
                flg = 0;
                break;
            }
        }
        if(flg) return l;
    }
    return 0;
}
inline void Matrix_qsm(int y){
    while(y){
        if(y&1){
            for(int i = 1; i <= n; ++i){
                for(int j = 1; j <= n; ++j){
                    tmp[i][j] = INF;
                    for(int k = 1; k <= n; ++k){
                        tmp[i][j] = min(tmp[i][j], ans[i][k] + g[k][j]);
                    }
                }
            }
            for(int i = 1; i <= n; ++i){
                for(int j = 1; j <= n; ++j){
                    ans[i][j] = tmp[i][j];
                }
            }
        }
        y /= 2;
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= n; ++j){
                tmp[i][j] = INF;
                for(int k = 1; k <= n; ++k){
                    tmp[i][j] = min(tmp[i][j], g[i][k] + g[k][j]);
                }
            }
        }
        for(int i = 1; i <= n; ++i){
            for(int j = 1; j <= n; ++j){
                g[i][j] = tmp[i][j];
            }
        }
    }
}
signed main(){
    // freopen("file.in","r",stdin);
    cin >> n >> m;
    for(int i = 1; i <= n; ++i){
        cin >> s[i];
        len[i] = s[i].size();
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            g[i][j] = len[j] - calc_same(s[j],s[i],i==j);
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            if(i == j) ans[i][j] = 0;
            else ans[i][j] = INF;
        }
    }
    Matrix_qsm(m-1);
    Ans = INF;
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            Ans = min(Ans,len[i]+ans[i][j]);
        }
    }
    printf("%lld\n",Ans);
    return 0;
}

转载于:https://www.cnblogs.com/qixingzhi/p/11233027.html

你可能感兴趣的:([POI2010] CHO-Hamsters)