LOJ #6074. 「2017 山東一輪集訓 Day6」子序列

#6074. 「2017 山東一輪集訓 Day6」子序列

分析:html

  首先設f[i][j]爲到第i個點,結尾字符是j的方案數,這個j必定是從i往前走,第一個出現的j,由於這個j能夠代替掉前面全部j。因而有轉移方程:ios

$$ f_{i,j}= \begin{cases} f_{i-1,j}&,j\neq S_i\\ \sum_{k=1}^{m+1}f_{i-1,k}&,j=S_i \end{cases} $$git

  表示若是當前j不是s[i]的話,最靠後的結尾的j仍是那個位置,從i-1轉移便可,不然,最靠後的s[i]變成i這個位置,因而加上前面全部最靠後出現的字符便可(即從i往前走到k,若是s[k]在k+1~i之間沒有s[k]了,就加上)。ui

  這個dp還有一個形象的解釋:每一個i向它後面第一個出現的字符連有向邊(即若是i->j有邊,那麼i+1~j之間沒有s[[j]),而後DAG上的路徑數就是答案。spa

  而後能夠對每一個位置求一個$10 \times 10$的轉移矩陣$A_i$,$F_i$是一個$10 \times 1$的矩陣,有$F_i = A_i \times F_{i - 1}$。code

  因而能夠分別維護矩陣的前綴積,和逆矩陣的前綴積。htm

  而後複雜度能夠作到$nc^3+qc^2$,因爲這個矩陣的性質,能夠$nc^2$預處理,因此能夠作到$nc^2+qc^2$,至於如何作到$nc+qc$,能夠看這blog

代碼:get

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<bitset>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 100005, mod = 1e9 + 7;
struct Matrix{ int a[10][10]; Matrix() { memset(a, 0, sizeof(a)); } } s1[N], s2[N];
char str[N];
int s[N], tmp[15];

inline void add(int &x,int y) { x += y; if (x >= mod) x -= mod; }
inline void sub(int &x,int y) { x -= y; if (x < 0) x += mod; }
void init(int n) {
    for (int i = 0; i < 10; ++i) s1[0].a[i][i] = s2[0].a[i][i] = 1;
    for (int i = 0; i < 10; ++i) s1[1].a[i][i] = 1;
    for (int i = 0; i < 10; ++i) s1[1].a[s[1]][i] = 1;
    for (int i = 2; i <= n; ++i) {
        s1[i] = s1[i - 1];
        for (int j = 0; j < 10; ++j) tmp[j] = 0;
        for (int j = 0; j < 10; ++j) 
            for (int k = 0; k < 10; ++k) add(tmp[j], s1[i - 1].a[k][j]);
        for (int j = 0; j < 10; ++j) s1[i].a[s[i]][j] = tmp[j];
    }
    for (int i = 0; i < 10; ++i) s2[1].a[s[1]][i] = mod - 1;
    for (int i = 0; i < 10; ++i) s2[1].a[i][i] = 1;
    for (int i = 2; i <= n; ++i) {
        s2[i] = s2[i - 1];
        for (int j = 0; j < 10; ++j) for (int k = 0; k < 10; ++k) if (k != s[i]) sub(s2[i].a[j][k], s2[i - 1].a[j][s[i]]);
    }
}
int main() {
    scanf("%s", str + 1);
    int n = strlen(str + 1);
    for (int i = 1; i <= n; ++i) s[i] = str[i] - 'a';
    init(n);
    for (int m = read(); m --; ) {
        int l = read(), r = read();
        for (int i = 0; i < 10; ++i) tmp[i] = 0; 
        for (int i = 0; i < 10; ++i) add(tmp[i], s2[l - 1].a[i][9]);
        int sum = 0;
        for (int i = 0; i < 10; ++i) 
            for (int j = 0; j < 10; ++j) 
                add(sum, 1ll * s1[r].a[i][j] * tmp[j] % mod);            
        cout << (sum - 1 + mod) % mod << "\n";
     }
    return 0;
}
相關文章
相關標籤/搜索