ACM模板

动态规划

背包

01背包

//物品数量为n 背包容量为v
//第i个物品的价值为val[i] 体积为vol[i]
//最终结果为dp[v]
const int maxn = 1010;
int dp[maxn];
int vol[maxn];
int val[maxn];
int n, v;

void solve() {
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < n; i++) {
        for(int j = v; j >= vol[i]; j--) {
            dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
        }
    }
}

完全背包

const int maxn = 1010;
int dp[maxn];
int vol[maxn];
int val[maxn];
int n, v;

void solve() {
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < n; i++) {
        for(int j = vol[i]; j <= v; j++) {
            dp[j] = max(dp[j], dp[j - vol[i]] + val[i]);
        }
    }
}

多重背包

//恰好装满 hdu2844
#include<iostream>
#include<algorithm>
#include<climits>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 110;
const int maxm = 100010;
int val[maxn];
int num[maxn];
int dp[maxm];
int n, m;

void solve() {
    memset(dp, -1, sizeof(dp));
    dp[0] = 0;
    for(int i = 0; i < n; i++) {
        if(val[i] * num[i] >= m) {
            for(int j = val[i]; j <= m; j++) {
                if(dp[j - val[i]] != -1) {
                    dp[j] = max(dp[j], dp[j - val[i]] + val[i]);
                }
            }
        }
        else {
            int cnt = 1;
            int totVal = val[i];
            while(num[i] > cnt) {
                for(int j = m; j >= totVal; j--) {
                    if(dp[j - totVal] != -1) {
                        dp[j] = max(dp[j], dp[j - totVal] + totVal);
                    }
                }
                num[i] -= cnt;
                cnt <<= 1;
                totVal <<= 1;
            }
            totVal = num[i] * val[i];
            for(int j = m; j >= totVal; j--) {
                if(dp[j - totVal] != -1) {
                    dp[j] = max(dp[j], dp[j - totVal] + totVal);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++) {
        if(dp[i] == i) {
            ans++;
        }
    }
    printf("%d\n", ans);
}

int main() {
    while(scanf("%d%d", &n, &m) && (n || m)) {
        for(int i = 0; i < n; i++) {
            scanf("%d", val + i);
        }
        for(int i = 0; i < n; i++) {
            scanf("%d", num + i);
        }
        solve();
    }
    return 0;
}

多重背包判断可行性

//每种有若干件的物品能否填满给定容量的背包
//O(NV)
// hdu2844
#include<iostream>
#include<algorithm>
#include<climits>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 101;
const int maxm = 100001;
int val[maxn];
int num[maxn];
int dp[maxm];
int n, m;

void solve() {
    for(int i = 1; i <= m; i++) {
        dp[i] = -1;
    }
    dp[0] = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j <= m; j++) {
            dp[j] = dp[j] >= 0 ? num[i] : -1;
        }
        for(int j = 0; j <= m - val[i]; j++) {
            if(dp[j] > 0) {
                dp[j + val[i]] = max(dp[j + val[i]], dp[j] - 1);
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= m; i++) {
        if(dp[i] != -1) {
            ans++;
        }
    }
    printf("%d\n", ans);
}

int main() {
    while(scanf("%d%d", &n, &m) && (n || m)) {
        for(int i = 1; i <= n; i++) {
            scanf("%d", val + i);
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", num + i);
        }
        solve();
    }
    return 0;
}

数论

组合数求模

卢卡斯定理

//FZU 2020
//要求p为素数
//适合使用的场景:
//1 <= m <= n <= 1e18 , p <= 1e5
typedef long long LL;
LL quickPowMod(LL n, LL r, LL mod) {
    LL res = 1;
    n %= mod;
    while(r) {
        if(r & 1) {
            res = (res * n) % mod;
        }
        n = (n * n) % mod;
        r >>= 1;
    }
    return res;
}

LL inverse(LL n, LL mod) {
    return quickPowMod(n, mod - 2, mod);
}

LL C(LL n, LL m, LL mod) {
    if(m > n) {
        return 0;
    }
    LL res = 1;
    LL a, b;
    for(int i = 1; i <= m; i++) {
        a = (n + i - m) % mod;
        b = inverse(i, mod);
        res = (((res * a) % mod)* b) % mod;
    }
    return res;
}

LL lucas(LL n, LL m, LL mod) {
    if(m == 0) {
        return 1;
    }
    return C(n % mod, m % mod, mod) * lucas(n / mod, m / mod, mod) % mod;
}

int main() {
    int t;
    LL n, m, mod;
    cin >> t;
    while(t--) {
        cin >> n >> m >> mod;
        cout << lucas(n, m, mod) << endl;
    }
    return 0;
}

扩展卢卡斯定理

C n m % p &ThickSpace;&ThickSpace; 1 ≤ m ≤ n ≤ 1 0 18 &ThickSpace;&ThickSpace; 2 ≤ p ≤ 1000000 &ThickSpace;&ThickSpace; 且 p 不 保 证 为 质 数 C_n^m \% p\;\;1\leq m \leq n \leq 10^{18}\;\;2\leq p \leq 1000000\;\;且p不保证为质数 Cnm%p1mn10182p1000000p

//洛谷4720模板
//Gym - 100633J
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

LL quickPowerMod(LL n, LL r, LL p) {
    LL res = 1;
    n %= p;
    while(r) {
        if(r & 1) {
            res = res * n % p;
        }
        r >>= 1;
        n = n * n % p;
    }
    return res;
}

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = exgcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a / b * y;
    return res;
}

LL inverse(LL a, LL m) {
    LL x1, x2;
    LL d = exgcd(a, m, x1, x2);
    if(d != 1) {
        return -1;
    }
    return (x1 % m + m) % m;
}

LL CRT(vector<LL> &b, vector<LL> &m) {
    LL n = m.size();
    LL ans = 0;
    LL M = 1;
    LL Mi;
    LL x, y;
    for(LL temp : m) {
        M *= temp;
    }
    for(LL i = 0; i < n; i++) {
        Mi = M / m[i];
        //exgcd(Mi, m[i], x, y);
        ans = (ans + b[i] * Mi * inverse(Mi, m[i])) % M;
    }
    ans = (ans + M) % M;
    return ans;
}

//计算n!中因子p的个数
LL calculateExp(LL n, LL p) {
    LL res = 0;
    while(n) {
        res += n / p;
        n /= p;
    }
    return res;
}

//计算n!中除去所有因子p之后模mod(p^x)的值
LL facModP(LL n, LL mod, LL p) {
    if(n == 0) {
        return 1;
    }
    LL res = 1;
    if(n / mod) {
        for(LL i = 2; i <= mod; i++) {
            if(i % p != 0) {
                res = res * i % mod;
            }
        }
        res = quickPowerMod(res, n / mod, mod);
    }
    LL temp = n % mod;
    for(LL i = 2; i <= temp; i++) {
        if(i % p != 0) {
            res = res * i % mod;
        }
    }
    return res * facModP(n / p, mod, p) % mod;
}

//计算C(n, m) % p^x 的值
LL CmodP(LL n, LL m, LL mod, LL p) {
    LL res, inv1, inv2;
    LL cnt = 0;
    cnt += calculateExp(n, p);
    cnt -= calculateExp(m, p);
    cnt -= calculateExp(n - m, p);

    res = facModP(n, mod, p);
    inv1 = facModP(m, mod, p);
    inv2 = facModP(n - m, mod, p);
    inv1 = inverse(inv1, mod);
    inv2 = inverse(inv2, mod);
    res = (res * inv1 % mod) * inv2 % mod;
    res = res * quickPowerMod(p, cnt, mod) % mod;
    return res;
}

//计算C(n, m) % mod(合数)的值
LL CmodX(LL n, LL m, LL mod) {
    vector<LL> b, md;
    LL temp;
    for(LL i = 2; i * i <= mod; i++) {
        if(mod % i == 0) {
            temp = 1;
            while(mod % i == 0) {
                temp *= i;
                mod /= i;
            }
            md.push_back(temp);
            b.push_back(CmodP(n, m, temp, i));
        }
    }
    if(mod > 1) {
        md.push_back(mod);
        b.push_back(CmodP(n, m, mod, mod));
    }
    return CRT(b, md);
}

int main() {
    LL n, m, p;
    while(cin >> n >> m >> p) {
        cout << CmodX(n, m, p) << endl;
    }
    return 0;
}

模线性方程组

中国剩余定理

typedef long long LL;
const int maxn = 1000;
LL b[maxn];
LL m[maxn];

LL extendGcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = extendGcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return res;
}

LL CRT(LL *b, LL *m, int n) {
    LL ans = 0;
    LL M = 1;
    LL Mi;
    LL x, y;
    for(int i = 0; i < n; i++) {
        M *= m[i];
    }
    for(int i = 0; i < n; i++) {
        Mi = M / m[i];
        extendGcd(Mi, m[i], x, y);
        ans = (ans + b[i] * Mi * x) % M;
    }
    ans = (ans + M) % M;
    return ans;
}

扩展中国剩余定理

//模板验证POJ2891

typedef long long LL;
const int maxn = 1000;
LL b[maxn];
LL m[maxn];

LL exGcd(LL a, LL b, LL &x, LL &y) {
    if(b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    LL res = exGcd(b, a % b, x, y);
    LL temp = x;
    x = y;
    y = temp - a/b*y;
    return res;
}

LL gcd(LL a, LL b) {
    return b == 0 ? a : gcd(b, a % b);
}

LL inverse(LL a, LL m) {
    LL x1, x2;
    LL d = exGcd(a, m, x1, x2);
    if(d != 1) {
        return -1;
    }
    return x1 % m;
}

bool merge(LL m1, LL b1, LL m2, LL b2, LL &m3, LL &b3) {
    LL d = gcd(m1, m2);
    LL b = b2 - b1;
    if(b % d) {
        return false;
    }
    b = (b % m2 + m2) % m2;
    m1 /= d;
    m2 /= d;
    b /= d;
    b *= inverse(m1, m2);
    b %= m2;
    b *= m1 * d;
    b += b1;
    m3 = m1 * m2 * d;
    b3 = (b % m3 + m3) % m3;
    return true;
}

LL CRT(LL *b, LL *m, int n) {
    LL tempm, tempb;
    for(int i = 0; i < n - 1; i++) {
        if(merge(m[i], b[i], m[i + 1], b[i + 1], tempm, tempb)) {
            m[i + 1] = tempm;
            b[i + 1] = tempb;
        }
        else {
            return -1;
        }
    }
    return (b[n - 1] % m[n - 1] + m[n - 1]) % m[n - 1];
}

int main() {
    int n;
    while(cin >> n) {
        for(int i = 0; i < n; i++) {
            cin >> m[i] >> b[i];
        }
        cout << CRT(b, m, n) << endl;
    }
    return 0;
}

欧拉筛

欧拉筛筛选素数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int countPrim;
void sieve() {
    int temp;
    memset(isPrim, -1, sizeof(isPrim));
    isPrim[1] = false;
    countPrim = 0;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                break;
            }
        }
    }
}

欧拉筛计算欧拉函数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int phi[maxn];
int countPrim;
void sieve() {
    LL temp;
    memset(isPrim, -1, sizeof(isPrim));
    memset(phi, 0, sizeof(phi));
    phi[1] = 1;
    countPrim = 0;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                phi[temp] = phi[i]*primes[j];
                break;
            }
            else {
                phi[temp] = phi[i]*phi[primes[j]];
            }
        }
    }
}

欧拉筛计算莫比乌斯函数

const int maxn = 100000;
bool isPrim[maxn];
int primes[maxn];
int mu[maxn];
int countPrm;
void sieve() {
    memset(isPrim, -1, sizeof(isPrim));
    memset(mu, 0, sizeof(mu));
    memset(primes, 0, sizeof(primes));
    countPrim = 0;
    isPrim[1] = false;
    mu[1] = 1;
    LL temp;
    for(int i = 2; i < maxn; i++) {
        if(isPrim[i]) {
            primes[countPrim++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < countPrim && (temp = i * primes[j]) < maxn; j++) {
            isPrim[temp] = false;
            if(i % primes[j] == 0) {
                mu[temp] = 0;
                break;
            }
            else {
                mu[temp] = -mu[i];
            }
        }
    }
}

Gauss-Jordan消元

const double eps = 1e-8;
const int maxn = 110;
typedef double Matrix[maxn][maxn];

void guassJordan(Matrix A, int n) {
    int i, j, k, r;
    for(i = 0; i < n; i++) {
        r = i;
        for(j = i + 1; j < n; j++) {
            if(fabs(A[j][i]) > fabs(A[r][i])) {
                r = j;
            }
        }
        if(fabs(A[r][i]) < eps) {
            continue;
        }
        if(r != i) {
            for(j = 0; j <= n; j++) {
                swap(A[r][j], A[i][j]);
            }
        }
        for(k = 0; k < n; k++) {
            if(k != i) {
                for(j = n; j >= i; j--) {
                    A[k][j] -= A[k][i] / A[i][i] * A[i][j];
                }
            }
        }
    }
}

FFT NTT FWT

FFT

//模板验证 HDU1402 模拟大数乘法
#include<iostream>
#include<algorithm>
#include<complex>
#include<cstdio>
#include<cstring>
#include<cstring>
using namespace std;

const int maxm = 17;
const int maxn = 1 << maxm;
complex<double> buffer1[maxn];
complex<double> buffer2[maxn];
complex<double> omega[maxn];
complex<double> conjOmega[maxn];
char number1[maxn];
char number2[maxn];
int sum[maxn];

void initOmega(int n) {
    double pi = acos(-1);
    for(int i = 0; i < n; i++) {
        omega[i] = complex<double>(cos(2 * pi * i / n), sin(2 * pi * i / n));
        conjOmega[i] = conj(omega[i]);
    }
}

void inverse(int n, complex<double>* buffer) {
    for(int i = 0, j = 0; i < n; i++) {
        if(i < j) {
            swap(buffer[i], buffer[j]);
        }
        for(int k = n >> 1; (j ^= k) < k; k >>= 1) {
            continue;
        }
    }
}

void fft(int n, complex<double>* buffer, complex<double>* omega) {
    inverse(n, buffer);
    for(int i = 2; i <= n; i <<= 1) {
        int m = i >> 1;
        int step = n / i;
        for(int j = 0; j < n; j += i) {
            for(int k = 0; k < m; k++) {
                complex<double> temp = buffer[j + k];
                buffer[j + k] = buffer[j + k] + omega[step * k] * buffer[j + k + m];
                buffer[j + k + m] = temp - omega[step * k] * buffer[j + k + m];
            }
        }
    }
}

int main() {
    int len1, len2, len;
    while(scanf("%s%s", number1, number2) == 2) {
        len1 = strlen(number1);
        len2 = strlen(number2);
        for(len = 1; len < len1 * 2 || len < len2 * 2; len <<= 1) {
            continue;
        }
        for(int i = 0; i < len1; i++) {
            buffer1[i] = number1[len1 - i - 1] - '0';
        }
        for(int i = len1; i < len; i++) {
            buffer1[i] = 0;
        }
        for(int i = 0; i < len2; i++) {
            buffer2[i] = number2[len2 - i - 1] - '0';
        }
        for(int i = len2; i < len; i++) {
            buffer2[i] = 0;
        }
        initOmega(len);
        fft(len, buffer1, omega);
        fft(len, buffer2, omega);
        for(int i = 0; i < len; i++) {
            buffer1[i] *= buffer2[i];
        }
        fft(len, buffer1, conjOmega);
        for(int i = 0; i < len; i++) {
            sum[i] = (int)(buffer1[i].real() / len + 0.5);
        }
        for(int i = 0; i < len; i++) {
            sum[i + 1] += sum[i] / 10;
            sum[i] %= 10;
        }
        for(len = len1 + len2 - 1; len > 0 && sum[len] <= 0; len--) {
            continue;
        }
        for(int i = len; i >= 0; i--) {
            printf("%d", sum[i]);
        }
        putchar('\n');
    }
    return 0;
}

NTT

//模板验证 HDU1402 模拟大数乘法
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstring>
using namespace std;
typedef long long LL;

const LL MOD = 1004535809;
const LL gOfMod = 3;
const LL maxm = 17;
const LL maxn = 1 << maxm;
LL g[maxn];
LL invG[maxn];
LL buffer1[maxn];
LL buffer2[maxn];
char number1[maxn];
char number2[maxn];
LL sum[maxn];

LL quickPowerMod(LL n, LL r, LL mod) {
    LL res = 1;
    n %= mod;
    while(r) {
        if(r & 1) {
            res = res * n % mod;
        }
        n = n * n % mod;
        r >>= 1;
    }
    return res;
}

void initG(LL n) {
    g[0] = 1;
    g[1] = quickPowerMod(3, (MOD - 1) / n, MOD);
    invG[0] = 1;
    invG[1] = quickPowerMod(g[1], MOD - 2, MOD);
    for(LL i = 2; i < n; i++) {
        g[i] = g[i - 1] * g[1] % MOD;
        invG[i] = invG[i - 1] * invG[1] % MOD;
    }
}

void inverseBuffer(LL n, LL* buffer) {
    for(LL i = 0, j = 0; i < n; i++) {
        if(i > j) {
            swap(buffer[i], buffer[j]);
        }
        for(int k = n >> 1; (j ^= k) < k; k >>= 1) {
            continue;
        }
    }
}

void fft(LL n, LL* buffer, LL* g) {
    inverseBuffer(n, buffer);
    for(LL i = 2; i <= n; i <<= 1) {
        LL m = i / 2;
        LL step = n / i;
        for(int j = 0; j < n; j+= i) {
            for(int k = 0; k < m; k++) {
                LL temp = buffer[j + k];
                buffer[j + k] = (buffer[j + k] + g[k * step] * buffer[j + k + m] % MOD) % MOD;
                buffer[j + k + m] = ((temp - g[k * step] * buffer[j + k + m]) % MOD + MOD) % MOD;
            }
        }
    }
}

int main() {
    LL len1, len2, len;
    while(scanf("%s%s", number1, number2) == 2) {
        len1 = strlen(number1);
        len2 = strlen(number2);
        for(len = 1; len < len1 * 2 || len < len2 * 2; len <<= 1) {
            continue;
        }
        for(int i = 0; i < len1; i++) {
            buffer1[i] = number1[len1 - i - 1] - '0';
        }
        for(int i = len1; i < len; i++) {
            buffer1[i] = 0;
        }
        for(int i = 0; i < len2; i++) {
            buffer2[i] = number2[len2 - i - 1] - '0';
        }
        for(int i = len2; i < len; i++) {
            buffer2[i] = 0;
        }
        initG(len);
        fft(len, buffer1, g);
        fft(len, buffer2, g);
        for(int i = 0; i < len; i++) {
            buffer1[i] = buffer1[i] * buffer2[i] % MOD;
        }
        fft(len, buffer1, invG);
        LL invLen = quickPowerMod(len, MOD - 2, MOD);
        for(int i = 0; i < len; i++) {
            sum[i] = buffer1[i] * invLen % MOD;
        }
        for(int i = 0; i < len; i++) {
            sum[i + 1] += sum[i] / 10;
            sum[i] %= 10;
        }
        for(len = len1 + len2 - 1; len > 0 && sum[len] <= 0; len--) {
            continue;
        }
        while(len >= 0) {
            printf("%I64d", sum[len--]);
        }
        putchar('\n');
    }
    return 0;
}

数据结构

线段树

线段树单点更新

非递归建树
/*
模板验证 HDU1166
求区间和,题目给的区间描述是左闭右闭的,代码中全部使用左开右闭的区间
所有下标从0开始
非递归建树建立的是一颗满二叉树
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

#define Add "Add"
#define Sub "Sub"
#define End "End"
#define Query "Query"

using namespace std;

const int maxn = 50050;
const int inf = 0x7fffffff;
int date[maxn * 4];
int leaves;	//叶子节点数量
int n;
char order[6];

void init() {
    leaves = 1;
    while(leaves < n) {
        leaves <<= 1;
    }
    memset(date, 0, sizeof(date));
}

void update(int index, int val) {
    index += leaves - 1;       //原数组中下标在线段树对应的数组中下标值的计算方法
    date[index] += val;
    while(index > 0) {
        index = (index - 1) / 2;
        date[index] += val;
    }
}

//要求查询的区间为[a, b), 当前查询的区间为[l, r),当前区间对应的节点为k
//在主函数中调用时调用方法为query(a, b, 0, 0, leaves);
int query(int a, int b, int k, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    if(a <= l && b >= r) {
        return date[k];
    }
    return query(a, b, 2 * k + 1, l, (l + r)/2) + query(a, b, 2 * k + 2, (l + r) / 2, r);
}

int main() {
    int t, temp, index, l ,r;
    scanf("%d", &t);
    for(int cs = 1; cs <= t; cs++) {
        scanf("%d", &n);
        init();
        for(int i = 0; i < n; i++) {
            scanf("%d", &temp);
            update(i, temp);
        }
        printf("Case %d:\n", cs);
        while(scanf("%s", order)) {
            if(strcmp(order, End) == 0) {
                break;
            }
            else if(strcmp(order, Add) == 0) {
                scanf("%d%d", &index, &temp);
                update(index - 1, temp);
            }
            else if(strcmp(order, Sub) == 0) {
                scanf("%d%d", &index, &temp);
                update(index - 1, -temp);
            }
            else if(strcmp(order, Query) == 0) {
                scanf("%d%d", &l, &r);
                printf("%d\n", query(l - 1, r, 0, 0, leaves));
            }
        }
    }
    return 0;
}

递归建树
/*
模板验证 HDU1166
求区间和,题目给的区间描述是左闭右闭的,代码中全部使用左开右闭的区间
所有下标从0开始
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

#define Add "Add"
#define Sub "Sub"
#define End "End"
#define Query "Query"

using namespace std;

const int maxn = 50050;
const int inf = 0x7fffffff;
int date[maxn * 4];
int sourceDate[maxn];
int n;
char order[6];

//当前节点为v,对应区间为[l, r)
void build(int v, int l, int r) {
    if(r - l == 1) {
        date[v] = sourceDate[l];
    }
    else {
        build(2 * v + 1, l, (l + r) / 2);
        build(2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

//需要修改原数组中下标为index的元素,变化的值为val
void update(int index, int val, int v, int l, int r) {
    if(r - l == 1) {
        date[v] += val;
    }
    else {
        if(index < (l + r) / 2) {
            update(index, val, v * 2 + 1, l, (l + r) / 2);
        }
        else {
            update(index, val, v * 2 + 2, (l + r) / 2, r);
        }
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

int query(int a, int b, int v, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    if(a <= l && b >= r) {
        return date[v];
    }
    return query(a, b, 2 * v + 1, l, (l + r) / 2) + query(a, b, 2 * v + 2, (l + r) / 2, r);
}

int main() {
    int t, index, val, l, r;
    scanf("%d", &t);
    for(int cs = 1; cs <= t; cs++) {
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
        }
        build(0, 0, n);
        printf("Case %d:\n", cs);
        while(scanf("%s", order) == 1) {
            if(strcmp(order, End) == 0) {
                break;
            }
            else if(strcmp(order, Add) == 0) {
                scanf("%d%d", &index, &val);
                update(index - 1, val, 0, 0, n);
            }
            else if(strcmp(order, Sub) == 0) {
                scanf("%d%d", &index, &val);
                update(index - 1, -val, 0, 0, n);
            }
            else if(strcmp(order, Query) == 0) {
                scanf("%d%d", &l, &r);
                printf("%d\n", query(l - 1, r, 0, 0, n));
            }
        }
    }
    return 0;
}

线段树区间更新

/*
模板验证POJ3468
计算区间和 
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

const int maxn = 100010;
LL sourceDate[maxn];	//原始数据
LL date[maxn << 2];	//存储线段树
LL mark[maxn << 2];	//懒惰标记

void build(int v, int l, int r) {
    mark[v] = 0;
    if(r - l == 1) {
        date[v] = sourceDate[l];
    }
    else {
        build(2 * v + 1, l, (l + r) / 2);
        build(2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

//下移懒惰标记
void pushDown(int v, int l, int r) {
    if(mark[v] != 0) {
        int chl = 2 * v + 1;
        int chr = 2 * v + 2;
        int mid = (l + r) / 2;
        mark[chl] += mark[v];
        mark[chr] += mark[v];
        date[chl] += (mid - l) * mark[v];
        date[chr] += (r - mid) * mark[v];
        mark[v] = 0;
    }
}

void update(int a, int b, int val, int v, int l, int r) {
    if(a >= r || b <= l) {
        return ;
    }
    else if(a <= l && b >= r) {
        mark[v] += val;
        date[v] += val * (r - l);
    }
    else {
        pushDown(v, l, r);
        update(a, b, val, 2 * v + 1, l, (l + r) / 2);
        update(a, b, val, 2 * v + 2, (l + r) / 2, r);
        date[v] = date[2 * v + 1] + date[2 * v + 2];
    }
}

LL query(int a, int b, int v, int l, int r) {
    if(a >= r || b <= l) {
        return 0;
    }
    else if(a <= l && b >= r) {
        return date[v];
    }
    else {
        pushDown(v, l, r);
        return query(a, b, 2 * v + 1, l, (l + r) / 2) + query(a, b, 2 * v + 2, (l + r) / 2, r);
    }
}

int main() {
    int n, m;
    char order;
    int a, b, v;
    while(scanf("%d%d", &n, &m) == 2) {
        for(int i = 0; i < n; i++) {
            scanf("%lld", sourceDate + i);
        }
        build(0, 0, n);
        for(int i = 0; i < m; i++) {
            scanf("\n%c", &order);
            if(order == 'Q') {
                scanf("%d%d", &a, &b);
                printf("%lld\n", query(a- 1, b, 0, 0, n));
            }
            else {
                scanf("%d%d%d", &a, &b, &v);
                update(a - 1, b, v, 0, 0, n);
            }
        }
    }
    return 0;
}

线段树+扫描线

HDU1542
//线段树 + 扫描线 + 离散化
//注意这里的区间是闭区间
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
struct Edge {
    double xl;
    double xr;
    double h;
    int flag;
    bool operator<(const Edge &e) {
        return h < e.h;
    }
};

const int maxn = 110;
double len[maxn << 3];
int cover[maxn << 3];
Edge edge[maxn << 1];
double point[maxn << 1];

void build() {
    memset(len, 0, sizeof(len));
    memset(cover, 0, sizeof(cover));
}

void update(int a, int b, int val, int v, int l, int r) {
    if(a >= r || b <= l) {
        return ;
    }
    else if(a <= l && b >= r) {
        cover[v] += val;
        if(cover[v]) {
            len[v] = point[r] - point[l];
        }
        else if(r - l > 1) {
            len[v] = len[2 * v + 1] + len[2 * v + 2];
        }
        else {
            len[v] = 0;
        }
    }
    else {
        update(a, b, val, 2 * v + 1, l, (l + r) / 2);
        update(a, b, val, 2 * v + 2, (l + r) / 2, r);
        if(cover[v]) {
            len[v] = point[r] - point[l];
        }
        else {
            len[v] = len[2 * v + 1] + len[2 * v + 2];
        }
    }
}

int main() {
    int n;
    double x1, x2, y1, y2;
    int cnt;
    int cs = 0;
    double ans;
    int a, b;
    while(scanf("%d", &n) && n) {
        for(int i = 0; i < n; i++) {
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);

            edge[2 * i].xl = edge[2 * i + 1].xl = x1;
            edge[2 * i].xr = edge[2 * i + 1].xr = x2;
            edge[2 * i].flag = 1;
            edge[2 * i + 1].flag = -1;
            edge[2 * i].h = y1;
            edge[2 * i + 1].h = y2;

            point[2 * i] = x1;
            point[2 * i + 1] = x2;
        }
        sort(edge, edge + 2 * n);
        sort(point, point + 2 * n);
        cnt = 0;
        for(int i = 1; i < 2 * n; i++) {
            if(point[i] != point[cnt]) {
                point[++cnt] = point[i];
            }
        }
        build();
        ans = 0.0;
        for(int i = 0; i < 2 * n - 1; i++) {
            a = lower_bound(point, point + cnt + 1, edge[i].xl) - point;
            b = lower_bound(point, point + cnt + 1, edge[i].xr) - point;
            update(a, b, edge[i].flag, 0, 0, cnt);
            ans += len[0] * (edge[i + 1].h - edge[i].h);
            //printf("%f %f\n", len[0], edge[i + 1].h - edge[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++cs, ans);
    }
    return 0;
}

可持久化线段树 无更新

//模板验证 HDU2665
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    int ls;
    int rs;
    int val;
};

const int maxn = 100010;
Node date[maxn * 20];
int roots[maxn];
int sourceDate[maxn];
int sortDate[maxn];
int cntNode;
int sz;

void build(int &v, int l, int r) {
    v = cntNode++;
    date[v].val = 0;
    if(r - l == 1) {
        return ;
    }
    build(date[v].ls, l, (l + r) / 2);
    build(date[v].rs, (l + r) / 2, r);
}

void update(int pre, int &now, int l, int r, int val) {
    now = cntNode++;
    int mid = (l + r) / 2;
    date[now].ls = date[pre].ls;
    date[now].rs = date[pre].rs;
    date[now].val = date[pre].val + 1;
    if(r - l == 1) {
        return ;
    }
    if(val < mid) {
        update(date[pre].ls, date[now].ls, l, mid, val);
    }
    else {
        update(date[pre].rs, date[now].rs, mid, r, val);
    }
}

int query(int k, int lv, int rv, int l, int r) {
    if(r - l == 1) {
        return l;
    }
    int mid = (l + r) / 2;
    int res = date[date[rv].ls].val - date[date[lv].ls].val;
    if(k <= res) {
        return query(k, date[lv].ls, date[rv].ls, l, mid);
    }
    else {
        return query(k - res, date[lv].rs, date[rv].rs, mid, r);
    }
}

int main() {
    int t;
    int n, m;
    int l, r, k;
    int ans;
    int val;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
            sortDate[i] = sourceDate[i];
        }
        sort(sortDate, sortDate + n);
        sz = unique(sortDate, sortDate + n) - sortDate;
        cntNode = 0;
        build(roots[0], 0, sz);
        for(int i = 0; i < n; i++) {
            val = lower_bound(sortDate, sortDate + sz, sourceDate[i]) - sortDate;
            update(roots[i], roots[i + 1], 0, sz, val);
        }
        for(int i = 0; i < m; i++) {
            scanf("%d%d%d", &l, &r, &k);
            ans = query(k, roots[l - 1], roots[r], 0, sz);
            printf("%d\n", sortDate[ans]);
        }
    }
    return 0;
}

可持久化线段树 单点更新

//ZOJ2112
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    int ls;
    int rs;
    int val;
};

struct Operation {
    char order;
    int l;
    int r;
    int k;
};

const int maxn = 50050;
const int maxm = 10010;
Node date[(maxn + maxm) * 32];  //主席树上的节点
Operation op[maxm];             //存储操作
int roots[maxn];                //主席树各个版本的根节点
int bits[maxn];                 //使用树状数组维护的各个主席树的根节点
int sourceDate[maxn + maxm];    //原始序列
int sortDate[maxn + maxm];      //离散化之后的序列
vector<int> li, ri;             //查询和修改时需要访问的主席树(使用树状数组维护的)的下标
char order[3];                  //暂时存储输入的命令'Q'和'C'
int cntNode;                    //统计所有的主席树的节点数量
int sz;                         //保存sortDate数组的大小
int n, m;

void build(int &v, int l, int r) {  //节点date[v]维护的区间为[l, r)
    v = cntNode++;
    date[v].val = 0;
    if(r - l == 1) {
        return ;
    }
    build(date[v].ls, l, (l + r) / 2);
    build(date[v].rs, (l + r) / 2, r);
}

void update(int pre, int &now, int l, int r, int k, int val) {
//now 是当前建立的节点,pre是前一棵树上与now对应的节点,date[now]维护的区间是[l, r)
//当前插入的值在离散化区间中对应的数字为k
//如果是向序列中插入k,则val = 1
//如果是将k替换为其它数字,则val = -1
    now = cntNode++;
    date[now].ls = date[pre].ls;
    date[now].rs = date[pre].rs;
    date[now].val = date[pre].val + val;
    if(r - l == 1) {
        return ;
    }
    int mid = (l + r) / 2;
    if(k < mid) {
        update(date[pre].ls, date[now].ls, l, mid, k, val);
    }
    else {
        update(date[pre].rs, date[now].rs, mid, r, k, val);
    }
}

void add(int index, int val) {
//当有修改操作时需要调用该函数
//index指的是被修改的数字在原序列中的下标(从1开始计数)
//我的下标是从0开始的,所以在计算离散化后它对应的数字的时候,使用index - 1
//如果当前的sourceDate[index - 1]是还没修改的数字,则val = -1;否则val = 1
    int k = lower_bound(sortDate, sortDate + sz, sourceDate[index - 1]) - sortDate;
    while(index <= n) {
        update(bits[index], bits[index], 0, sz, k, val);
        index += index & -index;
    }
}

int query(int sv, int ev, int l, int r, int k) {
//若查询的区间为[s + 1, e],则在组函数中调用该函数时
//sv 为第s棵线段树的根节点下标
//ev 为第e棵线段树的根节点下标(从0开始计数)
//sv和ev所维护的区间为[l, r)
//函数询问的是区间[l, r)中第k小的数(从1开始计数)
    if(r - l == 1) {
        return l;
    }
    int res = 0;

//下面两个循环采用了树状数组的思维进行求和
    for(int i : ri) {
        res += date[date[i].ls].val;
    }
    for(int i : li) {
        res -= date[date[i].ls].val;
    }

    res += date[date[ev].ls].val - date[date[sv].ls].val;
    if(k <= res) {
        for(auto it = ri.begin(); it != ri.end(); it++) {
            *it = date[*it].ls;
        }
        for(auto it = li.begin(); it != li.end(); it++) {
            *it = date[*it].ls;
        }
        return query(date[sv].ls, date[ev].ls, l, (l + r) / 2, k);
    }
    else {
        for(auto it = ri.begin(); it != ri.end(); it++) {
            *it = date[*it].rs;
        }
        for(auto it = li.begin(); it != li.end(); it++) {
            *it = date[*it].rs;
        }
        return query(date[sv].rs, date[ev].rs, (l + r) / 2, r, k - res);
    }
}

int main() {
    int t;
    int k, ans, temp;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        sz = 0;
        for(int i = 0; i < n; i++) {
            scanf("%d", sourceDate + i);
            sortDate[sz++] = sourceDate[i];
        }
        for(int i = 0; i < m; i++) {
            scanf("%s", order);
            op[i].order = order[0];
            if(order[0] == 'Q') {
                scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
            }
            else {
                scanf("%d%d", &op[i].l, &op[i].r);
                sortDate[sz++] = op[i].r;
            }
        }
        sort(sortDate, sortDate + sz);
        sz = unique(sortDate, sortDate + sz) - sortDate;
        cntNode = 0;
        build(roots[0], 0, sz);
        for(int i = 0; i < n; i++) {
            k = lower_bound(sortDate, sortDate + sz, sourceDate[i]) - sortDate;
            update(roots[i], roots[i + 1], 0, sz, k, 1);
        }
        for(int i = 0; i <= n; i++) {
            //注意这里是 <= ,树状数组维护的是n + 1棵线段树
            bits[i] = roots[0];
        }
        for(int i = 0; i < m; i++) {
            if(op[i].order == 'Q') {
                li.clear();
                ri.clear();

                //下面两个循环计算的是在query函数中
                //使用树状数组思维求和时
                //需要访问的线段树根节点下标
                temp = op[i].r;
                while(temp) {
                    ri.push_back(bits[temp]);
                    temp -= temp & -temp;
                }

                temp = op[i].l - 1;
                while(temp) {
                    li.push_back(bits[temp]);
                    temp -= temp & -temp;
                }
                ans = query(roots[op[i].l - 1], roots[op[i].r], 0, sz, op[i].k);
                printf("%d\n", sortDate[ans]);
            }
            else {
                add(op[i].l, -1);
                sourceDate[op[i].l - 1] = op[i].r;
                add(op[i].l, 1);
            }
        }
    }
    return 0;
}

树状数组

一维树状数组

一维树状数组单点更新
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

const int maxn = 500050;
LL bit[maxn];

void add(int k, int val) {
    while(k < maxn) {
        bit[k] += val;
        k += k & -k;
    }
}

LL sum(int k) {
    LL res = 0;
    while(k > 0) {
        res += bit[k];
        k -= k & -k;
    }
    return res;
}

int main() {
}
一维树状数组区间更新
//模板验证 HDU1556
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100010;
int bit0[maxn];
int bit1[maxn];
int n;

void pointAdd(int *bit, int k, int val) {
    while(k <= n) {
        bit[k] += val;
        k += k & -k;
    }
}

int sum(int *bit, int k) {
    int res = 0;
    while(k > 0) {
        res += bit[k];
        k -= k & -k;
    }
    return res;
}

void rangeAdd(int l, int r, int val) {
    pointAdd(bit0, l, -val * (l - 1));
    pointAdd(bit1, l, val);
    pointAdd(bit0, r + 1, val * r);
    pointAdd(bit1, r + 1, -val);
}

int rangeSum(int l, int r) {
    return  sum(bit0, r) + r * sum(bit1, r) -
            sum(bit0, l - 1) - (l - 1) * sum(bit1, l - 1);
}

int main() {
    int l, r;
    while(scanf("%d", &n) && n) {
        memset(bit0, 0, sizeof(bit0));
        memset(bit1, 0, sizeof(bit1));
        for(int i = 0; i < n; i++) {
            scanf("%d%d", &l, &r);
            rangeAdd(l, r, 1);
        }
        for(int i = 1; i < n; i++) {
            printf("%d ", rangeSum(i, i));
        }
        printf("%d\n", rangeSum(n, n));
    }
    return 0;
}

二维树状数组

二维树状数组单点更新
const int maxn = 1000;
int bit[maxn][maxn];

void add(int x, int y, int val) {
    int memoryY = y;
    while(x <= maxn) {
        y = memoryY;
        while(y <= maxn) {
            bit[x][y] += val;
            y += y & -y;
        }
        x += x & -x;
    }
}

int sum(int x, int y) {
    int memoryY = y;
    int res = 0;
    while(x > 0) {
        y = memoryY;
        while(y > 0) {
            res += bit[x][y];
            y -= y & -y;
        }
        x -= x & -x;
    }
    return res;
}

字符串

KMP

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 400001;
char source[maxn];
char pattern[maxn];
int next[maxn];
/*
int violentMatch(char *s, char *p) {
    int slen = strlen(s);
    int plen = strlen(p);
    int si = 0;
    int pi = 0;
    while(si < slen && pi < plen) {
        if(s[si] == p[pi]) {
            si++;
            pi++;
        }
        else {
            si = si - pi + 1;
            pi = 0;
        }
    }
    if(pi == plen) {
        return si - pi;
    }
    else {
        return -1;
    }
}
*/
void setNext(char *p, int *nx) {
    int plen = strlen(p);
    nx[0] = -1;
    int k = -1;
    int j = 0;
    while(j < plen - 1) {
        if(k == -1 || p[j] == p[k]) {
            k++;
            j++;
            if(p[j] != p[k]) {
                next[j] = k;
            }
            else {
                next[j] = next[k];
            }
        }
        else {
            k = next[k];
        }
    }
}
//判断p是否在s中,使用的next数组是p的next数组
//返回p在s中第一次出现的位置(0开始) 若s中没有p,则返回-1
int KMP(char *s, char *p) {
    int slen = strlen(s);
    int plen = strlen(p);
    int si = 0;
    int pi = 0;
    while(si <= slen - (plen - pi) && pi < plen) {
        if(pi == -1 || s[si] == p[pi]) {
            si++;
            pi++;
        }
        else {
            pi = next[pi];
        }
    }
    if(pi == plen) {
        return si - pi;
    }
    else {
        return -1;
    }
}

int main() {
    return 0;
}

扩展KMP

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace sdt;

const int maxn = 1000;
char source[maxn];
char pattern[maxn];
int next[maxn];
int extend[maxn];

//求解setNext使用 abcabc 作为子串,自己手动使用该算法算一遍应该就懂了...
void setNext(char *str, int *nx) {
    int len = strlen(str);
    nx[0] = len;
    for(nx[1] = 0; nx[1] < n - 1 && str[nx[1]] == str[nx[1] + 1]; nx[1]++) {
        continue;
    }
    int p0 = 1, p = nx[1] + 1 - 1;
    for(int i = 2; i < len; i++) {
        if(nx[i - p0] + i - 1 < p) {
            nx[i] = nx[i - p0];
        }
        else {
            nx[i] = max(0, p - i + 1);
            while(i + nx[i] < len && str[nx[i]] == str[i + nx[i]]) {
                nx[i]++;
            }
            p0 = i;
            p = i + nx[i] - 1;
        }
    }
}

void exKMP(char *sc, char *pat) {
    setNext(pat, next);
    int sLen = strlen(sc);
    int pLen = strlen(pat);
    for(extend[0] = 0; extend[0] < sLen && extend[0] < pLen && sc[extend[0]] == pat[extend[0]]; extend[0]++) {
        continue;
    }
    int p0 = 0;
    int p = extend[0] - 1;
    for(int i = 1; i < sLen; i++) {
        if(next[i - p0] + i - 1 < p) {
            extend[i] = next[i - p0];
        }
        else {
            int j = p - i + 1;
            if(j < 0) {
                j = 0;
            }
            while(j < pLen && i + j < sLen && sc[i + j] == pat[j]) {
                j++;
            }
            p0 = i;
            p = i + j - 1;
        }
    }
}

Manacher

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 1000;
char source[maxn];          //原始字符串数据
char tempStr[maxn << 1];    //转换后的字符串
int maxLen[maxn << 1];      //maxLen[i]为以tempStr[i]为中心的最长回文子串最右边的位置到tempStr[i]的距离

int manacher() {
    int len = strlen(source);
    for(int i = 0; i < len; i++) {
        tempStr[2 * i] = '#';
        tempStr[2 * i + 1] = source[i];
    }
    tempStr[2 * len] = '#';
    tempStr[2 * len + 1] = '\0';
    len = 2 * len + 1;
    memset(maxLen, 0, sizeof(maxLen));
    maxLen[0] = 1;
    int p0 = 0, p = 0, ans = 0;     //p为当前匹配到了的最远的字符的下标,p0为取得该最远位置的中心的下标
    for(int i = 1; i < len; i++) {
        if(i < p) {
            if(i  + maxLen[2 * p0 - i] - 1 < p) {
                maxLen[i] = maxLen[2 * p0 - i];
            }
            else {
                maxLen[i] = p - i + 1;
                while(i - maxLen[i] >= 0 && i + maxLen[i] < len && tempStr[i - maxLen[i]] == tempStr[i + maxLen[i]]) {
                    maxLen[i]++;
                }
                if(i + maxLen[i] > p0 + maxLen[p0]) {
                    p0 = i;
                    p = i + maxLen[i] - 1;
                }
            }
        }
        else {
            maxLen[i] = 1;
            while(i - maxLen[i] >= 0 && i + maxLen[i] < len && tempStr[i - maxLen[i]] == tempStr[i + maxLen[i]]) {
                maxLen[i]++;
            }
            if(i + maxLen[i] > p0 + maxLen[p0]) {
                p0 = i;
                p = i + maxLen[i] - 1;
            }
        }
        ans = max(ans, maxLen[i] - 1);
    }
    return ans;
}

字典树

const alphNum = 26;
const int maxn = 1000;
int trie[maxn][alphNum];

void insert(const char *s) {
    int len = strlen(s);
    root = 0;
    int id;
    for(int i = 0; i < len; i++) {
        id = s[i] - 'a';
        if(tire[root][id] == 0) {
            trie[root][id] = ++tot;
        }
        root = trie[root][id];
    }
}

bool find(const char *s) {
    int len = strlen(s);
    int root = 0;
    int id;
    for(int i = 0; i < len; i++) {
        id = s[i] - 'a';
        if(trie[root][id] == 0) {
            return false;
        }
        root = trie[root][id];
    }
    return true;
}

AC自动机

//板子验证 HDU2222
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
class Node {
public:
    Node *next[26];
    Node *fail;
    int cnt;    //本来觉得用bool就好了,但是看了HDU2222之后,发现还是用int好一点
    Node() {
        for(int i = 0; i < 26; i++) {
            next[i] = NULL;
        }
        fail = NULL;
        cnt = 0;
    }
};

class ACauto {
public:
    Node *root;

    ACauto() {
        root = new Node;
    }

    ~ACauto() {
        clear();
        delete root;
    }

    void clear() {
        Node* temp;
        queue<Node *> q;
        q.push(root);
        while(!q.empty()) {
            temp = q.front();
            q.pop();
            for(int i = 0; i < 26; i++) {
                if(temp->next[i]) {
                    q.push(temp->next[i]);
                }
            }
            if(temp != root) {
                delete temp;
            }
        }
        for(int i = 0; i < 26; i++) {
            root->next[i] = NULL;
        }
    }

    void insert(const char * s) {
        Node *p = root;
        int id;
        for(int i = 0; s[i]; i++) {
            id = s[i] - 'a';
            if(p->next[id] == NULL) {
                p->next[id] = new Node;
            }
            p = p->next[id];
        }
        p->cnt++;
    }

    void buildFail() {
        Node *father;
        Node *fatherFail;
        queue<Node *> q;
        q.push(root);
        while(!q.empty()) {
            father = q.front();
            q.pop();
            for(int i = 0; i < 26; i++) {
                if(father->next[i]) {
                    fatherFail = father->fail;
                    while(fatherFail) {
                        if(fatherFail->next[i]) {
                            father->next[i]->fail = fatherFail->next[i];
                            break;
                        }
                        fatherFail = fatherFail->fail;
                    }
                    if(fatherFail == NULL) {
                        father->next[i]->fail = root;
                    }
                    q.push(father->next[i]);
                }
            }
        }
    }

    int find(const char *s) {
        int res = 0;
        Node *p = root;
        Node *temp;
        int id;
        for(int i = 0; s[i]; i++) {
            id = s[i] - 'a';
            while(p->next[id] == NULL && p != root) {
                p = p->fail;
            }
            p = p->next[id];
            if(p == NULL) {
                p = root;
            }
            temp = p;
            while(temp != root && temp->cnt != -1) {
                res += temp->cnt;
                temp->cnt = -1;
                temp = temp->fail;
            }
        }
        return res;
    }
};

const int maxKeyLen = 55;
const int maxTextLen = 1000010;
char key[maxKeyLen];
char str[maxTextLen];
ACauto ac;

int main() {
    int t;
    int n;
    scanf("%d", &t);
    while(t--) {
        ac.clear();
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%s", key);
            ac.insert(key);
        }
        ac.buildFail();
        scanf("%s", str);
        printf("%d\n", ac.find(str));
    }
    return 0;
}

AC自动机+矩阵快速幂

/*
模板测试 POJ2778
在建立失效指针的同时,统计安全节点的数量
以此减小矩阵的规模
测试数据
比如当输入的字符串为
ATCG
T
的时候,安全节点只有两个,为root和A
此时cntNode = 2
*/
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;

class Node {
public:
    Node *next[4];
    Node *fail;
    bool isTail;
    int tag;
    Node() {
        memset(next, 0, sizeof(next));
        fail = NULL;
        isTail = false;
        tag = -1;
    }
};

class ACauto {
private:
    Node *root;
    map<char, int> trans;

public:
    int cntNode;    //统计安全节点的数量
    ACauto() {
        root = new Node();
        root->tag = 0;
        cntNode = 1;
        trans['A'] = 0;
        trans['C'] = 1;
        trans['T'] = 2;
        trans['G'] = 3;
    }

    ~ACauto() {
        clear();
        delete root;
    }

    void clear() {
        queue<Node *> que;
        Node *temp;
        que.push(root);
        while(!que.empty()) {
            temp = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(temp->next[i] != NULL) {
                    que.push(temp->next[i]);
                }
            }
            if(temp != root) {
                delete temp;
            }
        }
        for(int i = 0; i < 4; i++) {
            root->next[i] = NULL;
        }
        cntNode = 1;
    }

    void insert(const char *s) {
        Node *p = root;
        int id;
        for(int i = 0; s[i]; i++) {
            id = trans[s[i]];
            if(p->next[id] == NULL) {
                if(p->isTail) {
                    return ;
                }
                p->next[id] = new Node();
            }
            p = p->next[id];
        }
        p->isTail = true;
    }

    void buildFail() {
        Node *father;
        Node *fatherFail;
        queue<Node *> que;
        que.push(root);
        while(!que.empty()) {
            father = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(father->next[i] != NULL && !(father->next[i]->isTail)) {
                    fatherFail = father->fail;
                    while(fatherFail != NULL) {
                        if(fatherFail->next[i]) {
                            father->next[i]->fail = fatherFail->next[i];
                            break;
                        }
                        fatherFail = fatherFail->fail;
                    }
                    if(fatherFail == NULL) {
                        father->next[i]->fail = root;
                    }
                    if(father->next[i]->fail->isTail) {
                        father->next[i]->isTail = true;
                    }
                    else {
                        father->next[i]->tag = cntNode++;
                        que.push(father->next[i]);
                    }
                }
            }
        }
    }

    void buildMatrix(LL mat[][110]) {
        for(int i = 0; i < cntNode; i++) {
            for(int j = 0; j < cntNode; j++) {
                mat[i][j] = 0;
            }
        }
        queue<Node *> que;
        Node *father, *temp;
        que.push(root);
        while(!que.empty()) {
            father = que.front();
            que.pop();
            for(int i = 0; i < 4; i++) {
                if(father->next[i] != NULL && !(father->next[i]->isTail)) {
                    que.push(father->next[i]);
                }
                temp = father;
                while(temp != NULL && temp->next[i] == NULL) {
                   temp = temp->fail;
                }
                if(temp == NULL) {
                    mat[father->tag][0]++;
                }
                else if(temp->next[i]->tag >= 0){
                    mat[father->tag][temp->next[i]->tag]++;
                }
            }
        }
    }
};

const LL MOD = 100000;
char str[20];
LL matrix1[110][110];
LL matrix2[110][110];
LL matrix3[110][110];
ACauto ac;

void mul(LL matrix1[110][110], LL matrix2[110][110], int len) {
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix3[i][j] = 0;
        }
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j <len; j++) {
            for(int k = 0; k < len; k++) {
                matrix3[i][j] = ((matrix1[i][k] * matrix2[k][j] % MOD) + matrix3[i][j]) % MOD;
            }
        }
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix1[i][j] = matrix3[i][j];
        }
    }
}

void power(LL matrix1[][110], int n, int len) {
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            if(i != j) {
                matrix2[i][j] = 0;
            }
            else {
                matrix2[i][j] = 1;
            }
        }
    }
    while(n) {
        if(n & 1) {
            mul(matrix2, matrix1, len);
        }
        mul(matrix1, matrix1, len);
        n >>= 1;
    }
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            matrix1[i][j] = matrix2[i][j];
        }
    }
}


int main() {
    int m, n;
    LL ans;
    while(scanf("%d%d", &m, &n) == 2) {
        ac.clear();
        for(int i = 0; i < m; i++) {
            scanf("%s", str);
            ac.insert(str);
        }
        ac.buildFail();
        ac.buildMatrix(matrix1);
        power(matrix1, n, ac.cntNode);
        ans = 0;
        for(int j = 0; j < ac.cntNode; j++) {
            ans = (ans + matrix1[0][j]) % MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

后缀数组

//时间复杂度大约为(4~6)O(n * floor(log n))
#include<iostream>
#include<cstdio>
#include<cstring>
//#define DEBUG
using namespace std;

const int maxn = 1000;
char str[maxn];             //需要求后缀数组的字符串
int suffixArray[maxn];      //suffixArray[i]为排名第i的子串的起点
                            //在计算过程中,如果出现子串相等的情况,则起点靠前的子串排名靠前
int cnt[maxn];              //统计排名(_rank)为i的二元组的数量
int tempArray1[maxn];
int tempArray2[maxn];
int n;
int Rank[maxn];             //Rank[i]为后缀i的排名
int height[maxn];           //height[i]为后缀i和后缀i-1的最长公共前缀长度

#ifdef DEBUG
void show(int *a) {
    for(int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
}
#endif // DEBUG

void buildSa(int m) {
    int countKind;                      //统计二元组的种类
    int *_rank = tempArray1;            //_rank[i]为,以str[i]开头的长度为k的子串的排名
                                        //即刘汝佳《算法竞赛入门经典——训练指南》P220中每幅图最下面的一行
                                        //若子串相等,则排名相同

/* *************************************************************************************************** */
    memset(cnt, 0, sizeof(int) * m);
    for(int i = 0; i < n; i++) {        //cnt先暂时用来统计每种字符出现的数量
        cnt[(int)str[i]]++;
    }
    for(int i = 1; i < m; i++) {
        cnt[i] += cnt[i - 1];
    }
    for(int i = n - 1; i >= 0; i--) {   //计算名次为--cnt[str[i]]的子串的起点
        //1 获取字符串中第i个位置的字符
        //2 通过--cnt[str[i]]计算出以str[i]开头的长度为1的子串的排名
        //3 则排名为--cnt[str[i]]的子串的起点下标为i
        suffixArray[--cnt[(int)str[i]]] = i;
    }
    countKind = 0;
    for(int i = 0, preKey = -1; i < n; i++) {
        if(str[suffixArray[i]] == preKey) {
            _rank[suffixArray[i]] = countKind;
        }
        else {
            preKey = str[suffixArray[i]];
            _rank[suffixArray[i]] = ++countKind;
        }
    }
#ifdef DEBUG
    show(_rank);
    show(suffixArray);
#endif // DEBUG
    if(countKind == n) {
        return ;
    }
//到目前为止,都是为了计算出字串长度为1时的排名
//_rank在接下来会用于计算第一和第二关键字
//当前计算出的_rank就是下一次迭代中二元组的第一关键字
/* *************************************************************************************************** */
    for(int k = 1; k <= n; k <<= 1) {
        //每个子串的长度为2 * k
        int *firstKey = _rank;                                                  //上一重迭代中计算出的_rank就是当前迭代中二元组的第一关键字
        int *secondRankIndex = _rank == tempArray1 ? tempArray2 : tempArray1;   //secondRankIndex第二关键字排名第i的二元组的下标
        for(int i = 0; i < k; i++) {
            secondRankIndex[i] = n - k + i;
        }
        for(int i = 0, j = k; i < n; i++) {
            if(suffixArray[i] >= k) {
                secondRankIndex[j++] = suffixArray[i] - k;
            }
        }
        memset(cnt, 0, sizeof(int) * (countKind + 1));
        for(int i = 0; i < n; i++) {
            cnt[firstKey[i]]++;
        }
        for(int i = 1; i <= countKind; i++) {
            cnt[i] += cnt[i - 1];
        }
        for(int i = n - 1; i >= 0; i--) {
            //1 找到第二关键字排名第i的二元组的下标secondRankIndex[i],令index = secondRankIndex[i]
            //2 找到该二元组的第一关键字firstKey[index],令key = firstKey[index]
            //3 求出该二元组的排名--cnt[key]
            suffixArray[--cnt[firstKey[secondRankIndex[i]]]] = secondRankIndex[i];
        }
        //程序执行到这里的时候,_rank中保存的是上一重迭代的计算结果
        //_rank[i]即为firstKey[i]即为第i个二元组的第一关键字
        //当i>=k时,firstKey[i] = secondKey[i - k]即第i-k个二元组的第二关键字
        _rank = secondRankIndex;    //现在secondRankIndex用不上了,就把它的空间拿给_rank用
                                    //但是firstKey保持不变,依然是指向上一重迭代中计算出来的_rank
        int preFirstKey = -1, preSecondKey = -1;
        int curFirstKey, curSecondKey;
        countKind = 0;
        for(int i = 0; i < n; i++) {
            curFirstKey = firstKey[suffixArray[i]];
            curSecondKey = suffixArray[i] >= n - k ? 0 : firstKey[suffixArray[i] + k];
            if(curFirstKey == preFirstKey && curSecondKey == preSecondKey) {
                _rank[suffixArray[i]] = countKind;
            }
            else {
                preFirstKey = curFirstKey;
                preSecondKey = curSecondKey;
                _rank[suffixArray[i]] = ++countKind;
            }
        }
#ifdef DEBUG
        show(_rank);
        show(suffixArray);
#endif // DEBUG
        if(countKind >= n) {
            return ;
        }
    }
}

void getHeight() {
    /*
        通过h[i] >= h[i - 1] - 1线性计算height
        设suffix(k)是排在suffix(i - 1)前一名的后缀
        即Rank[k] = Rank[i - 1] - 1
        则它们的最长公共前缀的长度为h[i - 1]即height[Rank[i - 1]]
        那么suffix(k + 1)排在suffix(i)前面
        (当h[i - 1] <= 1时,原式显然成立,故下面均假设h[i - 1] > 1)
        并且suffix(k + 1)和suffix(i)的最长公共前缀为h[i - 1] - 1
        于是,suffix(i)与它前面一名的最长公共前缀至少为h[i - 1] - 1
    */
    for(int i = 0; i < n; i++) {
        Rank[suffixArray[i]] = i;
    }
    for(int i = 0, j, k = 0; i < n; i++) {
        if(k > 0) {
            k--;
        }
        if(Rank[i] == 0) {
            height[Rank[i]] = 0;
            k = 0;
            continue;
        }
        j = suffixArray[Rank[i] - 1];
        while(i + k < n && j + k < n && str[i + k] == str[j + k]) {
            k++;
        }
        height[Rank[i]] = k;
    }
}

int main() {
    while(cin >> str) {
        n = strlen(str);
        buildSa('z' + 1);
    }
    return 0;
}

回文树(回文自动机)

指针版(性能较数组版低)

//HDU3948
//249ms	22.9MB	2544B	
//计算不同回文子串的数量
//注意使用时字符串下标从1开始
//输入时需要scanf("%s", str + 1);
#include<iostream>
#include<string>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

class Node {
public:
    enum{SIZE = 26};
    int len;
    int failLen;
    Node* next[SIZE];
    Node* fail;

    Node(int _len = 0, int _failLen = -1, Node *f = NULL) {
        len = _len;
        failLen = _failLen;
        for(int i = 0; i < SIZE; i++) {
            next[i] = NULL;
        }
        fail = len == -1 ? this : f;
    }
};

class PalindromeTree{
public:
    Node* odd;
    Node* even;
    int countNode;

    PalindromeTree() {
        odd = new Node(-1);
        even = new Node(0, -1, odd);
        countNode = 0;
    }

    void build(const char *s) {
        clear();
        Node *p = odd;
        for(int i = 1; s[i]; i++) {
            while(s[i - p->len - 1] != s[i]) {
                p = p->fail;
            }
            int id = s[i] - 'a';
            if(p->next[id] != NULL) {
                p = p->next[id];
            }
            else {
                countNode++;
                Node *temp = p;
                p->next[id] = new Node(p->len + 2, p->failLen + 1);
                p = p->next[id];
                if(temp->len == -1) {
                    p->fail = even;
                }
                else {
                    temp = temp->fail;
                    while(s[i - temp->len - 1] != s[i]) {
                        temp = temp->fail;
                    }
                    p->fail = temp->next[id];
                }
            }
        }
    }

    void clear() {
        queue<Node *> que;
        que.push(odd);
        que.push(even);
        Node *temp;
        while(!que.empty()) {
            temp = que.front();
            que.pop();
            for(int i = 0; i < Node::SIZE; i++) {
                if(temp->next[i] != NULL) {
                    que.push(temp->next[i]);
                }
            }
            if(temp == odd || temp == even) {
                continue;
            }
            delete temp;
        }
        for(int i = 0; i < Node::SIZE; i++) {
            odd->next[i] = NULL;
            even->next[i] = NULL;
        }
        countNode = 0;
    }

    ~PalindromeTree() {
        clear();
        delete odd;
        delete even;
    }
};

const int maxn = 110000;
char str[maxn];
PalindromeTree pt;

int main() {
    int t;
    int cs = 0;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", str + 1);
        pt.build(str);
        printf("Case #%d: %d\n", ++cs, pt.countNode);
        pt.clear();
    }
    return 0;
}

数组版

//板子验证同上
//78ms	11.3MB	1586B	
#include<iostream>
#include<string>
#include<queue>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;

struct Node {
    enum{SIZE = 26};
    int len;
    int next[Node::SIZE];
    int fail;

    void init(int _len = 0, int _fail = -1) {
        len = _len;
        for(int i = 0; i < SIZE; i++) {
            next[i] = -1;
        }
        fail = len == -1 ? 0 : _fail;
    }
};

const int maxn = 100010;
Node tree[maxn];
char str[maxn];
int countNode;

void clear() {
    tree[0].init(-1, 0);
    tree[1].init(0, 0);
    countNode = 2;
}

void build(const char *s) {
    clear();
    int p = 0;
    for(int i = 1; s[i]; i++) {
        while(s[i - tree[p].len - 1] != s[i]) {
            p = tree[p].fail;
        }
        int id = s[i] - 'a';
        if(tree[p].next[id] != -1) {
            p = tree[p].next[id];
        }
        else {
            int temp = p;
            tree[p].next[id] = countNode;
            tree[countNode].init(tree[p].len + 2);
            p = tree[p].next[id];
            if(temp == 0) {
                tree[p].fail = 1;
            }
            else {
                temp = tree[temp].fail;
                while(s[i - tree[temp].len - 1] != s[i]) {
                    temp = tree[temp].fail;
                }
                tree[p].fail = tree[temp].next[id];
            }
            countNode++;
        }
    }
}

int main() {
    int t;
    int cs = 0;
    scanf("%d", &t);
    while(t--) {
        scanf("%s", str + 1);
        build(str);
        printf("Case #%d: %d\n",++cs, countNode - 2);
    }
    return 0;
}

后缀自动机

1、两个串的最长公共子串
2、统计出现次数最多的长度为n的串一共出现了多少次,使用基数排序
3、
4、

两个串的最长公共子串

//两个串的最长公共子串
//https://www.spoj.com/problems/LCS/en/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int SIZE = 26;
const int START = int('a');
const int maxn = 260000;
char str1[maxn];
char str2[maxn];
struct State {
    int len;
    int next[SIZE];
    int link;
}st[maxn * 2];
int sz;
int last;

void initSa() {
    sz = 1;
    last = 0;
    st[0].len = 0;
    st[0].link = -1;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(char c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int id = c - START;
    int p = last;
    for(; p != -1 && st[p].next[id] == -1; p = st[p].link) {
        st[p].next[id] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[id];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].link = st[q].link;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            for(; p != -1 && st[p].next[id] == q; p = st[p].link) {
                st[p].next[id] = clone;
            }
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}

int lcs() {
    initSa();
    for(int i = 0; str1[i]; i++) {
        addChar(str1[i]);
    }
    int p = 0;
    int ans = 0;
    int curLen = 0;
    int id;
    for(int i = 0; str2[i]; i++) {
        id = str2[i] - START;
        while(p && st[p].next[id] == -1) {
            p = st[p].link;
            curLen = st[p].len;
        }
        if(st[p].next[id] != -1) {
            p = st[p].next[id];
            curLen++;
        }
        ans = max(ans, curLen);
    }
    return ans;
}

int main() {
    while(scanf("%s%s", str1, str2) == 2) {
        printf("%d\n", lcs());
    }
    return 0;
}

多个串的最长公共子串

//LCS2 - Longest Common Substring II 
//https://www.spoj.com/problems/LCS2/en/
#include<iostream>
#include<algorithm>
#include<set>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 110000;
const int SIZE = 26;
struct State {
    int len;
    int matcheLen;
    int link;
    int next[SIZE];
}st[maxn * 2];
int sz;
int last;
char str[maxn];
int bucket[maxn];
int Rank[maxn * 2];

void initSa() {
    sz = 1;
    last = 0;
    st[0].len = 0;
    st[0].link = -1;
    st[0].matcheLen = 0;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(int c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    st[cur].matcheLen = 0;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int p = last;
    for(; p != -1 && st[p].next[c] == -1; p = st[p].link) {
        st[p].next[c] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[c];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].link = st[q].link;
            st[clone].matcheLen = 0;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            st[q].link = st[cur].link = clone;
            for(; p != -1 && st[p].next[c] == q; p = st[p].link) {
                st[p].next[c] = clone;
            }
        }
    }
    last = cur;
}

void match(const char* s) {
    int cur = 0;
    int curLen = 0;
    int id;
    for(int i = 0; s[i]; i++) {
        id = s[i] - 'a';
        while(cur && st[cur].next[id] == -1) {
            cur = st[cur].link;
            st[cur].matcheLen = curLen = st[cur].len;
        }
        if(st[cur].next[id] != -1) {
            curLen++;
            cur = st[cur].next[id];
            st[cur].matcheLen = max(st[cur].matcheLen, curLen);
        }
    }
    for(int i = 0; i < sz; i++) {
        if(st[Rank[i]].matcheLen) {
            st[st[Rank[i]].link].matcheLen = st[st[Rank[i]].link].len;
        }
        st[Rank[i]].len = min(st[Rank[i]].len, st[Rank[i]].matcheLen);
        st[Rank[i]].matcheLen = 0;
    }
}

void radixSort(int len) {
    for(int i = 0; i < sz; i++) {
        ++bucket[st[i].len];
    }
    for(int i = len - 1; i >= 0; i--) {
        bucket[i] += bucket[i + 1];
    }
    for(int i = 0; i < sz; i++) {
        Rank[--bucket[st[i].len]] = i;
    }
}

int main() {
    //freopen("wa.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    initSa();
    scanf("%s", str);
    int len;
    for(len = 0; str[len]; len++) {
        addChar(str[len] - 'a');
    }
    radixSort(len);
    while(scanf("%s", str) == 1) {
        match(str);
    }
    int ans = 0;
    for(int i = 0; i < sz; i++) {
        ans = max(ans, st[i].len);
    }
    printf("%d\n", ans);
    return 0;
}

出现次数查询

//Substrings SPOJ - NSUBSTR 
//https://www.spoj.com/problems/NSUBSTR/en/
//使用基数排序
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 260000;
const int SIZE = 26;
struct {
    int len;
    int next[SIZE];
    int link;
    int cnt;
}st[maxn * 2];
int bucket[maxn];
int Rank[maxn * 2];
int ans[maxn];
char str[maxn];
int sz;
int last;

void initSa() {
    last = 0;
    sz = 1;
    st[0].cnt = 0;
    st[0].len = 0;
    st[0].link = -1;
    for(int i = 0; i < SIZE; i++) {
        st[0].next[i] = -1;
    }
}

void addChar(int c) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    st[cur].cnt = 1;
    for(int i = 0; i < SIZE; i++) {
        st[cur].next[i] = -1;
    }
    int p = last;
    for(; p != -1 && st[p].next[c] == -1; p = st[p].link) {
        st[p].next[c] = cur;
    }
    if(p == -1) {
        st[cur].link = 0;
    }
    else {
        int q = st[p].next[c];
        if(st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        }
        else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            st[clone].cnt = 0;
            st[clone].link = st[q].link;
            for(int i = 0; i < SIZE; i++) {
                st[clone].next[i] = st[q].next[i];
            }
            st[q].link = st[cur].link = clone;
            for(; p != -1 && st[p].next[c] == q; p = st[p].link) {
                st[p].next[c] = clone;
            }
        }
    }
    last = cur;
}

void radixSort(int len) {
    memset(bucket, 0, sizeof(bucket));
    for(int i = 0; i < sz; i++) {
        bucket[st[i].len]++;
    }
    for(int i = len - 1; i >= 0; i--) {
        bucket[i] += bucket[i + 1];
    }
    for(int i = 0; i < sz; i++) {
        Rank[--bucket[st[i].len]] = i;
    }
}

void solve() {
    initSa();
    int len;
    for(len = 0; str[len]; len++) {
        addChar(str[len] - 'a');
    }
    radixSort(len);
    for(int i = 0; i < sz; i++) {
        st[st[Rank[i]].link].cnt += st[Rank[i]].cnt;
    }
    memset(ans, 0, sizeof(ans));
    for(int i = 0; i < sz; i++) {
        ans[st[i].len] = max(ans[st[i].len], st[i].cnt);
    }
    for(int i = 1; i <= len; i++) {
        printf("%d\n", ans[i]);
    }
}

int main() {
    while(scanf("%s", str) == 1) {
        solve();
    }
    return 0;
}

1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An<O(N), O(1)> algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值