分类: OI笔记

  • 洛谷2257 YY的GCD 复习

    众所周知,这题是个人就会。

    $$ \text{钦定}N\le M \\ \sum_{1\le i\le N}{\sum_{1\le j\le M}{\left[ \mathrm{gcd}\left( i,j \right) =p \right]}} \\ \sum_p{\sum_{1\le pi\le N}{\sum_{1\le pj\le M}{\left[ \mathrm{gcd}\left( pi,pj \right) =p \right]}}} \\ \sum_p{\sum_{1\le i\le \lfloor \frac{N}{p} \rfloor}{\sum_{1\le j\le \lfloor \frac{M}{p} \rfloor}{\left[ \mathrm{gcd}\left( i,j \right) =1 \right]}}} \\ \sum_p{\sum_{1\le i\le \lfloor \frac{N}{p} \rfloor}{\sum_{1\le j\le \lfloor \frac{M}{p} \rfloor}{\sum_{k|gcd\left( i,j \right)}{\mu \left( k \right)}}}} \\ \sum_p{\sum_{1\le k\le \lfloor \frac{N}{p} \rfloor}{\sum_{1\le ik\le \lfloor \frac{N}{p} \rfloor}{\sum_{1\le jk\le \lfloor \frac{M}{p} \rfloor}{\sum_{k|gcd\left( ik,jk \right)}{\mu \left( k \right)}}}}} \\ \sum_p{\sum_{1\le k\le \lfloor \frac{N}{p} \rfloor}{\sum_{1\le i\le \lfloor \frac{N}{kp} \rfloor}{\sum_{1\le j\le \lfloor \frac{M}{kp} \rfloor}{\mu \left( k \right)}}}} \\ \sum_{1\le p\le N,p\,\,is\,\,prime}{\sum_{1\le k\le \lfloor \frac{N}{p} \rfloor}{\mu \left( k \right) \lfloor \frac{N}{kp} \rfloor \lfloor \frac{M}{kp} \rfloor}} \\ T=kp \\ T\le N \\ \sum_{1\le T\le N}{\sum_{p|T\left( T\text{的所有质因数} \right)}{\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor \mu \left( \frac{T}{p} \right)}} \\ =\sum_{1\le T\le N}{\lfloor \frac{N}{T} \rfloor \lfloor \frac{M}{T} \rfloor \sum_{p|T,\left( T\text{的所有质因数} \right)}{\mu \left( \frac{T}{p} \right)}} $$
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 2e7;
    
    int f[LARGE + 1];
    int fSum[LARGE + 1];
    int mu[LARGE + 1];
    int factor[LARGE + 1];
    bool isPrime[LARGE + 1];
    
    std::vector<int> primes;
    
    int_t query(int_t n, int_t m) {
        if (n > m)
            std::swap(n, m);
    #ifdef DEBUG
        cout << "process " << n << " " << m << endl;
    #endif
        int_t i = 1;
        int_t result = 0;
        while (i <= n) {
            int_t next = std::min(n / (n / i), m / (m / i));
            result += (n / i) * (m / i) * (fSum[next] - fSum[i - 1]);
            i = next + 1;
        }
        return result;
    }
    
    int main() {
        std::ios::sync_with_stdio(false);
        memset(isPrime, 1, sizeof(isPrime));
        isPrime[1] = false;
        for (int_t i = 2; i <= LARGE; i++) {
            if (isPrime[i]) {
                primes.push_back(i);
                for (int_t j = i * i; j <= LARGE; j += i) {
                    isPrime[j] = false;
                    if (factor[j] == 0)
                        factor[j] = i;
                }
                factor[i] = i;
            }
        }
        mu[1] = 1;
        for (int_t i = 2; i <= LARGE; i++) {
            int_t factor = ::factor[i];
            if (i / factor % factor == 0) {
                mu[i] = 0;
            } else {
                mu[i] = mu[i / factor] * -1;
            }
        }
        for (auto prime : primes) {
            for (int_t j = 1; j * prime <= LARGE; j++) {
                f[j * prime] += mu[j];
            }
        }
    #ifdef DEBUG
        for (int_t i = 2; i <= 100; i++) {
            cout << i << ": factor=" << factor[i] << " isPrime=" << isPrime[i]
                 << " mu=" << mu[i] << " f=" << f[i] << endl;
        }
    
    #endif
        for (int_t i = 2; i <= LARGE; i++)
            fSum[i] = fSum[i - 1] + f[i];
        int T;
        scanf("%d", &T);
        while (T--) {
            int n, m;
            scanf("%d%d", &n, &m);
            int_t result = query(n, m);
            printf("%lld\n", result);
        }
        return 0;
    }<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

     

  • 洛谷5357 AC自动机模板 二次加强 复习

    简单复习了以下AC自动机。

    首先注意,$$n$$个字符串构建的AC自动机至多可能有总串长+1个节点(根节点不隶属于任何一个字符串)

    由fail指针构成的树叫做后缀链接树,每个点的父节点为这个点所表示的字符串,在整个ACAM里能匹配到的公共后缀最长的字符串。(与SAM里的后缀连接好像是一样的….)

    构建后缀链接,并且把Trie树补成Trie图(即将每个点的不存在的子节点指向下一个待匹配位置的操作)通过BFS进行。

    初始时: 根节点的子节点的fail指针全都指向根,根节点的不存在的子节点构造关于根的自环。

    接下来根节点的存在的子节点入队,开始BFS。

    从队列里取出来队首元素V,按照如下方法操作:

    遍历其子节点,设当前遍历到的子节点通过字符chr获得,如果这个子节点存在,则其后缀链接指向V的后缀链接指向的节点的chr子节点,并将该节点入队。

    如果该子节点不存在,则将其替换为V的后缀链接的chr子节点。

     

    如果遇到一个所有字符串都没出现的字符怎么办?这时我们肯定会走到根,然后沿着根节点的子节点走自环,成功滚到下一个字符。

     

    匹配时直接沿着Trie图走即可(我们在BFS中已经把所有点的子节点补完了),每走到一个点,则说明了我们匹配到了根到这个点的边所构成的字符串。

     

    然后怎么统计每个子串出现的次数呢?

    首先我们在ACAM上的每个点标记上以这个点结尾的字符串,可以知道这种标记只会有总字符串个数个。

    然后我们构建出后缀链接树,我们可以知道,当我们的字符串走到某个点的时候,就意味着匹配到了这个点在后缀链接树上到树根经历的所有字符串,然后我们打个到根的差分标记,最后DFS统计下标记即可求出来每个字符串访问了几遍。

    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <string>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    struct Node {
        Node* chd[26];
        Node* fail = nullptr;
        // std::vector<int_t> strs;
        std::vector<int_t> strs;
        int_t mark = 0;
        Node*& access(char x) { return chd[x - 'a']; }
        Node() { memset(chd, 0, sizeof(chd)); }
        int_t id;
    };
    std::vector<int_t> graph[int_t(2e5) + 10];
    
    Node* mapping[int_t(2e5) + 10];
    Node* root = new Node;
    int_t used = 1;
    void insert(const char* str, int_t id) {
        Node* curr = root;
        for (auto ptr = str; *ptr; ptr++) {
            auto& chd = curr->access(*ptr);
            if (chd == nullptr) {
                chd = new Node;
                chd->id = ++used;
                mapping[used] = chd;
            }
            curr = chd;
        }
        curr->strs.push_back(id);
    }
    void buildFail() {
        std::queue<Node*> queue;
        // 安排根节点的子节点
        for (auto& ref : root->chd) {
            if (ref) {
                ref->fail = root;
                queue.push(ref);
                graph[1].push_back(ref->id);
            } else {
                ref = root;
            }
        }
        while (!queue.empty()) {
            Node* front = queue.front();
            queue.pop();
            for (char chr = 'a'; chr <= 'z'; chr++) {
                auto& ptr = front->access(chr);
                if (ptr) {
                    queue.push(ptr);
                    ptr->fail = front->fail->access(chr);
                    graph[ptr->fail->id].push_back(ptr->id);
    #ifdef DEBUG
                    cout << "edge " << ptr->fail->id << " to " << ptr->id << endl;
    #endif
                } else {
                    ptr = front->fail->access(chr);
                }
            }
        }
    }
    int_t counts[int_t(2e5 + 1)];
    void DFS(int_t vtx) {
        for (auto chd : graph[vtx]) {
            DFS(chd);
            mapping[vtx]->mark += mapping[chd]->mark;
        }
        Node* curr = mapping[vtx];
        for (auto x : curr->strs) {
            counts[x] += curr->mark;
        }
    }
    int main() {
        std::ios::sync_with_stdio(false);
        root->id = 1;
        mapping[1] = root;
        int_t n;
        cin >> n;
        static char buf[int_t(3e6 + 10)];
        for (int_t i = 1; i <= n; i++) {
            cin >> buf;
            insert(buf, i);
        }
        buildFail();
        cin >> buf;
        auto curr = root;
    
        for (auto ptr = buf; *ptr; ptr++) {
            auto chd = curr->access(*ptr);
            chd->mark += 1;
            curr = chd;
        }
        DFS(1);
        for (int_t i = 1; i <= n; i++)
            cout << counts[i] << endl;
        return 0;
    }

     

  • 洛谷1593 因子和

    直接质因数分解然后等比数列求和,再判一下公比模p为1就好了吧?

    #include <algorithm>
    #include <iostream>
    #include <map>
    using int_t = long long int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 9901;
    
    int_t power(int_t base, int_t index) {
        int_t result = 1;
        const int_t phi = mod - 1;
        if (index < 0) index = (index % phi + phi) % phi;
        while (index) {
            if (index & 1) result = result * base % mod;
            base = base * base % mod;
            index >>= 1;
        }
        return result;
    }
    std::map<int_t, int_t> factor(int_t n) {
        std::map<int_t, int_t> result;
        for (int_t i = 2; i * i <= n; i++) {
            while (n % i == 0) {
                result[i]++;
                n /= i;
            }
        }
        if (n != 1) result[n]++;
        return result;
    }
    int main() {
        int_t a, b;
        cin >> a >> b;
        auto primes = factor(a);
        int_t result = 1;
        for (const auto& kvp : primes) {
    #ifdef DEBUG
            cout << kvp.first << " " << kvp.second << endl;
            // cout << (power(kvp.first, kvp.second * b % mod + 1) - 1) << endl;
            // cout << result * (kvp.second * b % mod + 1) % mod << endl;
    #endif
    
            if (kvp.first % mod != 1) {
                result = result *
                         (power(kvp.first, kvp.second * b % (mod - 1) + 1) - 1) %
                         mod * (power(kvp.first - 1, -1)) % mod;
            } else {
                result = result * (kvp.second * b % mod + 1) % mod;
            }
        }
        cout << result << endl;
        return 0;
    }

     

  • CF1207F Remainder Problem

    /px

    被卡读入,自毙。

    按照下标分块,令块大小为$N$,则对于每一组询问$(x,y)$,当$x\geq N$时直接暴力枚举所有符合要求的点$xk+y$(不会超过$N$个);

    对于$x<N$的情况,我们维护一个二维数组$arr[x][y]$,表示$arr[x][y]$的答案,回答时直接输出即可。

    对于修改操作,我们直接枚举所有不超过$N$的数,分别取模后修改数组即可。

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <tuple>
    #include <utility>
    #include <vector>
    using int_t = long long int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    using pair_t = std::pair<int_t, int_t>;
    const int_t mod = 998244352;
    int_t power(int_t base, int_t index) {
        const int_t phi = mod - 1;
        index = (index % phi + phi) % phi;
        int_t result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    
    const int_t LARGE = 5e5;
    const int_t SQRT = 700;
    int_t n, m;
    int_t arr[LARGE + 1];
    int_t vecs[SQRT + 1][SQRT + 1];
    template <class T>
    void read(T& x) {
        x = 0;
        T flag = 1;
    
        char chr = getchar();
        while (chr != '-' && isdigit(chr) == false) chr = getchar();
        if (chr == '-') flag = -1;
        while (isdigit(chr) == false) chr = getchar();
        while (isdigit(chr)) {
            x = x * 10 + chr - '0';
            chr = getchar();
        }
        x *= flag;
    }
    template <class T>
    void write(T x) {
        if (x < 0) {
            x *= -1;
            putchar('-');
        }
        if (x > 9) write(x / 10);
        putchar('0' + x % 10);
    }
    int main() {
        std::ios::sync_with_stdio(false);
        int_t q;
        // cin >> q;
        read(q);
    
        while (q--) {
            int t, x, y;
            read(t);
            read(x);
            read(y);
            // cin >> t >> x >> y;
            // scanf("%d%d%d", &t, &x, &y);
            if (t == 1) {
                arr[x] += y;
                for (int_t i = 1; i <= SQRT; i++) vecs[i][x % i] += y;
            } else {
                if (x <= SQRT) {
                    // printf("%d\n", (int)vecs[x][y]);
                    // cout << (int)vecs[x][y] << endl;
                    write((int)vecs[x][y]);
                    putchar('\n');
                } else {
                    int_t result = 0;
                    for (int_t i = 0; i * x + y <= LARGE; i++) {
                        result += arr[i * x + y];
                    }
                    // printf("%d\n", int(result));
                    // cout << int(result) << endl;
                    write(int(result));
                    putchar('\n');
                }
            }
        }
        return 0;
    }

     

  • BZOJ4162 shlw loves matrix II

    $$ \text{对于矩阵}M,\text{构造其特征多项式}G\left( x \right) \\ \text{则有}M^n=G\left( M \right) Q\left( M \right) +R\left( M \right) \\ \text{即}M^n\equiv R\left( M \right) \left( mod\,\,G\left( M \right) \right) \\ \text{即}x^n\equiv R\left( x \right) \left( \text{mod }G\left( x \right) \right) \\ \text{考虑特征多项式如何计算。} \\ G\left( x \right) =\det \left( M-x\text{I} \right) ,\text{其中}I\text{为单位矩阵} \\ \text{由于}G\left( x \right) \text{为}n\text{次多项式}\left( n\text{为矩阵大小} \right) ,\text{故可以通过插值求出来对应的特征多项式。} \\
    \\ \text{然后多项式快速幂}+\text{取模即可} $$

    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <string>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1000000007;
    const int_t LARGE = 103;
    struct Matrix {
        int_t data[LARGE + 1][LARGE + 1];
        int_t n;
        Matrix(int_t n) {
            this->n = n;
            memset(data, 0, sizeof(data));
        }
        int_t at(int_t r, int_t c) const { return data[r][c]; }
        int_t* operator[](int_t r) { return data[r]; }
        Matrix operator*(const Matrix& rhs) const {
            Matrix result(n);
            for (int_t i = 1; i <= n; i++)
                for (int_t j = 1; j <= n; j++)
                    for (int_t k = 1; k <= n; k++) {
                        result[i][j] =
                            (result[i][j] + data[i][k] * rhs.at(k, j) % mod) % mod;
                    }
            return result;
        }
        Matrix operator+(const Matrix& rhs) const {
            Matrix result(n);
            for (int_t i = 1; i <= n; i++)
                for (int_t j = 1; j <= n; j++) {
                    result[i][j] = (data[i][j] + rhs.at(i, j)) % mod;
                }
            return result;
        }
        Matrix operator-() const {
            Matrix result = *this;
            for (int_t i = 1; i <= n; i++)
                for (int_t j = 1; j <= n; j++)
                    result[i][j] = (mod - result[i][j] + mod) % mod;
            return result;
        }
        Matrix operator*(int_t k) const {
            Matrix result = *this;
            for (int_t i = 1; i <= n; i++)
                for (int_t j = 1; j <= n; j++)
                    result[i][j] = (result[i][j] * k) % mod;
            return result;
        }
        Matrix operator-(const Matrix& rhs) const { return (*this) + (-rhs); }
    };
    int_t power(int_t base, int_t index) {
        if (index < 0) {
            index = (index % (mod - 1) + mod - 1) % (mod - 1);
        }
        int_t result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            base = base * base % mod;
            index >>= 1;
        }
        return result;
    }
    
    void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* C) {
        static int_t Cx[LARGE + 1];
        std::fill(Cx, Cx + n + m + 1, 0);
        for (int_t i = 0; i <= n; i++)
            for (int_t j = 0; j <= m; j++)
                Cx[i + j] = (Cx[i + j] + A[i] * B[j] % mod) % mod;
        std::copy(Cx, Cx + n + m + 1, C);
    }
    void poly_div(const int_t* A, int_t n, const int_t* B, int_t m, int_t* R,
                  int_t* Q) {
        while (B[m] % mod == 0) m--;
        if (n < m) {
            std::fill(Q, Q + n - m + 1, 0);
            std::copy(A, A + n + 1, R);
            return;
        }
        std::copy(A, A + n + 1, R);
        for (int_t i = n - m; i >= 0; i--) {
            Q[i] = R[i + m] * power(B[m], -1) % mod;
            for (int_t j = m; j >= 0; j--) {
                R[i + j] = (R[i + j] - Q[i] * B[j] % mod + mod) % mod;
            }
        }
    }
    void interpolate(int_t* X, int_t* Y, int_t n, int_t* result) {
        static int_t prod[LARGE + 1];
        prod[0] = 1;
        for (int_t i = 1; i <= n; i++) {
            int_t Z[] = {mod - X[i], 1};
            poly_mul(prod, i - 1, Z, 1, prod);
        }
    #ifdef DEBUG
        for (int_t i = 0; i <= n; i++) cout << prod[i] << " ";
        cout << endl;
    
    #endif
        for (int_t i = 1; i <= n; i++) {
            int_t coef = 1;
            for (int_t j = 1; j <= n; j++)
                if (i != j) {
                    coef = coef * (X[i] - X[j] + mod) % mod;
                }
            coef = Y[i] * power(coef, -1) % mod;
            static int_t curr[LARGE + 1], R[LARGE + 1];
            int_t B[] = {mod - X[i], 1};
            poly_div(prod, n, B, 1, R, curr);
    #ifdef DEBUG
            cout << "div x-" << X[i] << " = ";
            for (int_t i = 0; i <= n - 1; i++) cout << curr[i] << " ";
            cout << endl;
            cout << "coef = " << coef << endl;
            cout << endl;
    #endif
            for (int_t i = 0; i < n; i++)
                result[i] = (result[i] + coef * curr[i] % mod) % mod;
        }
    }
    int_t det(Matrix mat) {
        int_t prod = 1;
        for (int_t i = 1; i <= mat.n; i++) {
            int_t next = -1;
            for (int_t j = i; j <= mat.n; j++)
                if (mat[i][j]) {
                    next = j;
                    break;
                }
            if (next == -1) return 0;
            if (next != i) {
                prod *= -1;
                std::swap(mat.data[i], mat.data[next]);
            }
            for (int_t j = i + 1; j <= mat.n; j++) {
                int_t f = mat[j][i] * power(mat[i][i], -1) % mod;
                for (int_t k = i; k <= mat.n; k++) {
                    mat[j][k] = (mat[j][k] - f * mat[i][k] % mod + mod) % mod;
                }
            }
        }
        prod = mod + prod;
        for (int_t i = 1; i <= mat.n; i++) prod = prod * mat[i][i] % mod;
        return prod;
    }
    void poly_power(const int_t* A, int_t n, const int_t* B, int_t m,
                    const std::string& str, int_t* result) {
        static int_t base[LARGE + 1], Q[LARGE + 1];
        result[0] = 1;
        std::copy(A, A + n + 1, base);
        for (int_t i = 0; i < str.length(); i++) {
            if (str[i] == '1') {
                poly_mul(result, m - 1, base, m - 1, result);
                poly_div(result, 2 * m - 1, B, m, result, Q);
            }
            poly_mul(base, m - 1, base, m - 1, base);
            poly_div(base, 2 * m - 1, B, m, base, Q);
        }
    }
    int main() {
        std::string index;
        cin >> index;
        std::reverse(index.begin(), index.end());
    
        int_t n;
        cin >> n;
        Matrix mat(n), I(n);
        static int_t X[LARGE + 1], Y[LARGE + 1], result[LARGE + 1];
        for (int_t i = 1; i <= n; i++) {
            for (int_t j = 1; j <= n; j++) cin >> mat[i][j];
            I[i][i] = 1;
        }
    
        for (int_t i = 1; i <= 1 + n; i++) {
            X[i] = i;
    #ifdef DEBUG
            // auto curr=mat - I * i;
            // cout<<"sub "
    #endif
            Y[i] = det(mat - I * i);
    #ifdef DEBUG
    
            cout << "(" << X[i] << "," << Y[i] << ")" << endl;
    #endif
        }
        interpolate(X, Y, n + 1, result);
        // for (int_t i = 0; i <= n; i++) cout << result[i] << " ";
        // cout << endl;
        static int_t A[LARGE + 1];
        static int_t result_poly[LARGE + 1];
        A[1] = 1;
        poly_power(A, 1, result, n, index, result_poly);
    #ifdef DEBUG
        for (int_t i = 0; i < n; i++) cout << result_poly[i] << " ";
        cout << endl;
    #endif
        Matrix result_mat(n), curr = I;
        for (int_t i = 0; i < n; i++) {
            result_mat = result_mat + curr * result_poly[i];
            curr = curr * mat;
        }
        for (int_t i = 1; i <= n; i++) {
            for (int_t j = 1; j <= n; j++) {
                cout << result_mat[i][j] << " ";
            }
            cout << endl;
        }
        return 0;
    }

     

  • loj6485 LJJ学二项式定理

    $$ \sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^ia_{i\text{mod}4}}=\sum_{0\le j\le 3}{a_j\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\left[ i\equiv j\left( mod\,\,4 \right) \right]}} \\ =\sum_{0\le j\le 3}{\begin{array}{c} a_j\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\left[ i-j\equiv 0\left( mod\,\,4 \right) \right]}\\ \end{array}} \\ \text{令}\omega _4\text{为模998244353意义下的四次单位根,则有}\sum_{0\le i<3}{\omega _{4}^{i}}=0 \\ \text{显然对于任意非零的}k,\text{有}\sum_{0\le i<3}{\omega _{4}^{ik}}=\text{0,而对于}k=\text{0则有}\sum_{0\le i<3}{\omega _{4}^{ik}}=4 \\ \text{故}\sum_{0\le j\le 3}{a_j\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\left[ i-j\equiv 0\left( mod\,\,4 \right) \right]}}=\sum_{0\le j\le 3}{\begin{array}{c} a_j\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\frac{1}{4}\sum_{0\le k\le 3}{\omega _{4}^{k\left( i-j \right)}}}\\ \end{array}} \\ \text{当且仅当}i-j=0\left( \text{mod}4 \right) \text{时,有}\frac{1}{4}\sum_{0\le k\le 3}{\omega _{4}^{k\left( i-j \right)}}=1 \\ \sum_{0\le j\le 3}{\begin{array}{c} a_j\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\frac{1}{4}\sum_{0\le k\le 3}{\omega _{4}^{k\left( i-j \right)}}}\\ \end{array}}=\frac{1}{4}\sum_{0\le j\le 3}{a_j\sum_{0\le k\le 3}{\omega _{4}^{-kj}\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\omega _{4}^{ki}}}} \\ =\frac{1}{4}\sum_{0\le j\le 3}{\begin{array}{c} a_j\sum_{0\le k\le 3}{\omega _{4}^{-kj}\sum_{0\le i\le n}{\left( \begin{array}{c} n\\ i\\ \end{array} \right) s^i\left( \omega _{4}^{k} \right) ^i}}\\ \end{array}} \\ =\frac{1}{4}\sum_{0\le j\le 3}{\begin{array}{c} a_j\sum_{0\le k\le 3}{\omega _{4}^{-kj}\left( s\omega _{4}^{k}+1 \right) ^n}\\ \end{array}} \\ $$

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t mod = 998244353;
    
    int_t power(int_t base,int_t index) {
    	int_t result=1;
    	index=(index%(mod-1)+mod-1)%(mod-1);
    	while(index) {
    		if(index&1) result=result*base%mod;
    		index>>=1;
    		base=base*base%mod;
    	}
    	return result;
    }
    int main() {
    	int_t T;
    	cin>>T;
    	const int_t g4=power(3,(mod-1)/4);
    	while(T--) {
    		int_t n,s;
    		static int_t a[4];
    		scanf("%lld%lld",&n,&s);
    		for(int_t i=0; i<=3; i++)scanf("%lld",&a[i]);
    		int_t result=0;
    		for(int_t j=0; j<=3; j++) {
    			int_t sum=0;
    			for(int_t k=0; k<=3; k++) {
    				sum=(sum+power(g4,-k*j)*power(1+s*power(g4,k)%mod,n)%mod)%mod;
    			}
    			sum=sum*a[j]%mod;
    			result=(result+sum)%mod;
    		}
    		result=result*power(4,-1)%mod;
    		printf("%lld\n",result);
    	}
    	return 0;
    }

     

  • GZOI2019 旧词

    询问按照x从小到大分类。

    然后按照x递增处理,每次把根到x的路径上深度为p的点的值加上$p^k-(p-1)^k$。

    这样对于任何一条到根深度为p的路径,其权值为$p^k$。

    使用树剖+线段树维护。

    线段树的每个叶子节点有一个权值$p^k-(p-1)^k$,这个权值不会改变,同时需要维护区间$a_i\times(p_i^k-(p_i-1)^k)$的和。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t mod = 998244353;
    const int_t LARGE = 5e4;
    int_t power(int_t base,int_t index){
        int_t result=1;
        while(index) {
            if(index&1) result=result*base%mod;
            index>>=1;
            base=base*base%mod;
        }
        return result;
    }
    struct Node{
        int_t begin,end;
        int_t value = 0;
        Node*left=nullptr,*right=nullptr;
        int_t val1=0,val2=0,mark=0;
        Node(int_t begin,int_t end):begin(begin),end(end){}
        
        void add(int_t x){
            mark+=x;
            val1+=x;
            value=(value+val2*x%mod)%mod;
        }
        void pushDown(){
            if(begin!=end){
                left->add(mark);
                right->add(mark);
                mark=0;
            }
        }
        void maintain(){
            if(begin!=end){
                value=(left->value+right->value)%mod;
                val1=left->val1+right->val1;
                val2=(left->val2+right->val2)%mod;
            }
        }
        void add(int_t begin,int_t end,int_t x){
            if(end<this->begin||begin>this->end) return;
            if(this->begin>=begin&&this->end<=end){
                this->add(x);
                return;
            }
            pushDown();
            left->add(begin,end,x);
            right->add(begin,end,x);
            maintain();
        }
        int_t query(int_t begin,int_t end){
            if(end<this->begin||begin>this->end) return 0;
            if(this->begin>=begin&&this->end<=end) return this->value;
            pushDown();
            return (left->query(begin,end)+right->query(begin,end))%mod;
        }
        static Node* build(int_t begin,int_t end,int_t* arr){
            Node* node=new Node(begin,end);
            if(begin!=end){
                int_t mid=(begin+end)/2;
                node->left=build(begin,mid,arr);
                node->right=build(mid+1,end,arr);
            }else{
                node->val2=arr[begin];
            }
            node->maintain();
            return node;
        }
    };
    Node* root;
    int_t n,q,k;
    std::vector<int_t> graph[LARGE+1];
    int_t depth[LARGE+1],top[LARGE+1],maxChd[LARGE+1],size[LARGE+1];
    int_t parent[LARGE+1];
    int_t DFSN[LARGE+1],byDFSN[LARGE+1],target[LARGE+1];
    void DFS1(int_t vtx,int_t from=0,int_t depth=1){
        parent[vtx]=from,::depth[vtx]=depth;
        size[vtx]=1;
        for(int_t to:graph[vtx]){
            DFS1(to,vtx,depth+1);
            if(size[to]>size[maxChd[vtx]]) maxChd[vtx]=to;
            size[vtx]+=size[to];
        }
    }
    void DFS2(int_t vtx){
        DFSN[vtx]=++DFSN[0];
        byDFSN[DFSN[vtx]]=vtx;
        target[DFSN[vtx]] = (power(depth[vtx],k) - power(depth[parent[vtx]],k) + mod)%mod;
        if(maxChd[vtx]){
            top[maxChd[vtx]]=top[vtx];
            DFS2(maxChd[vtx]);
        }
        for(int_t to:graph[vtx]) if(to!=maxChd[vtx]){
            top[to]=to;
            DFS2(to);
        }
    }
    int_t query(int_t v1){
        int_t result=0;
        while(top[v1]){
            result+=root->query(DFSN[top[v1]],DFSN[v1]);
            result%=mod;
            v1=parent[top[v1]];
        }
        return result;
    }
    void add(int_t v1,int_t val=1){
        while(top[v1]){
            root->add(DFSN[top[v1]],DFSN[v1],val);
            v1=parent[top[v1]];
        }
    }
    int_t result[LARGE+1];
    struct Query{
        int_t prev, z;
        int_t* result;
    };
    std::vector<Query> querys[LARGE+1];
    int main() {
        scanf("%lld%lld%lld",&n,&q,&k);
        for(int_t i=2;i<=n;i++){
            int_t x;
            scanf("%lld",&x);
            graph[x].push_back(i);
        }
        for(int_t i=1;i<=q;i++){
            int_t x,z;
            scanf("%lld%lld",&x,&z);
            querys[x].push_back(Query{x,z,&result[i]});
        }
        DFS1(1);
        top[1]=1;
        DFS2(1);
        #ifdef DEBUG
        for(int_t i=1;i<=n;i++) cout<<"top "<<i<<" = "<<top[i]<<endl;
        #endif
        
        root=Node::build(1,n,target);
        for(int_t i=1;i<=n;i++){
            add(i);
            for(const auto& query:querys[i]){
                *query.result=::query(query.z);
            }
        }
        for(int_t i=1;i<=q;i++){
            printf("%lld\n",result[i]);
        }
        return 0;
    }

     

  • GZOI2019 与或和

    单调栈板子题..?

    首先按位拆分,然后转变为统计一个01矩阵中所有全1的子矩阵(按位与)以及所有全0的子矩阵(用总矩阵数减掉即为按位或的答案)。

    打表可知$n\times n$的矩阵有$(\frac{n(n+1)}2)^2$个子矩阵。

    统计全1矩阵和全0矩阵本质一样,故我们先考虑统计全0矩阵。

    考虑处理出来$f(i,j)$,表示如果第i行第j列的元素时0,那么往上到什么位置的元素也全都是0。对于是1的位置,$f(i,j)=-1$。

    然后我们枚举一个下界$low$,表示现在我们要统计下边缘在$low$上的矩形个数。

    然后从左到右扫,维护一个单调栈,单调栈内的元素是列,设$S_i$为栈中自底向上第$i$个元素,那么满足$f(low,i+1)>f(low,i)$。

    先不考虑出栈,考虑入栈。

    处理右下角为$(i,j)$时的答案时,设$x$表示合法的矩形数量

    假设第i列的元素$i$入栈的时候,我们设$S_{-1}$表示栈内最后一个元素,$S_{-2}$以此类推。

    如果加入元素i不会使得任何元素出栈,那么我们可以得到以$(i,j)$为右下角的所有合法的矩形数量为$x+f(low,i)$。

    由于加入i不会导致元素出栈,故$f(low,i)$一定大于栈中其他元素的值,所以第i列较低的$S_{-1}$列可以和前面的拼起来,比$S_{-1}$高的部分可以自己成一个宽度为1的矩形。

    如果导致了元素出栈呢?

    左侧到$S_{-2}$,上边界到$f(low,S_{-1})$的矩形不可能再次被取到,减掉即可。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    /*
    °´Î»ÌÖÂÛ
    ANDֵΪ1->¾ØÕóÈ«²¿Îª1
    ORֵΪ1->¾ØÕóÖÁÉÙÓÐÒ»¸ö1
    ͳ¼ÆÈ«0ºÍÈ«1µÄ¾ØÕó¸öÊý£¬ÖÁÉÙÓÐÒ»¸ö1µÈ¼ÛÓÚ×Ü×Ó¾ØÕóÊý-È«0¾ØÕó¡£
    */
    typedef int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    const int_t mod = 1e9 + 7;
    const int_t LARGE = 1e3+3;
    int_t mat[LARGE+1][LARGE+1];
    
    //last:ij¸öλÖÃÉÏ·½µÄµÚÒ»¸ö1µÄλÖÃ
    int_t count[LARGE+1][LARGE+1],last[2][LARGE+1][LARGE+1];
    int_t resultAnd,resultOr;
    int_t n;
    int_t C2(int_t n) {
    	return ((int64_t)n*(n-1)/2)%mod;
    }
    void countFor(int_t bit) {
    	//ö¾ÙÁÐ
    	for(int_t i=1; i<=n; i++) {
    		last[0][0][i]=last[1][0][i]=1;
    		for(int_t j=1; j<=n; j++) {
    			for(int_t k=0; k<=1; k++) {
    				if(count[j-1][i]!=k) {
    					last[k][j][i]=j;
    				} else {
    					last[k][j][i]=last[k][j-1][i];
    				}
    				if(count[j][i]!=k) last[k][j][i]=-1;
    			}
    		}
    	}
    	int_t zero=0,one=0;
    	//ö¾Ùµ×
    	for(int_t low=1; low<=n; low++) {
    		static int_t val[2][LARGE+1];
    		for(int_t i=1; i<=n; i++) {
    			for(int_t j=0; j<=1; j++) {
    				if(last[j][low][i]==-1) val[j][i]=0;
    				else val[j][i]=low-last[j][low][i]+1;
    			}
    		}
    		const auto count=[&](int_t bit) {
    			int_t result=0;
    			int_t count = 0;
    			//´Ó×óÍùÓÒɨ
    			std::vector<int_t> stack;
    			stack.push_back(0);
    			val[bit][0]=-1;
    			for(int_t i=1; i<=n; i++) {
    				//ºÍÇ°ÃæÁ¬µ½Ò»ÆðµÄ & ×Ô¼ºµÄ
    				count += val[bit][i];
    				while(stack.size() >= 1 && val[bit][stack.back()]>=val[bit][i]) {
    					count -= (stack.back() - stack[stack.size() - 2]) * (val[bit][stack.back()] - val[bit][i]);
    					stack.pop_back();
    				}
    				stack.push_back(i);
    				result += count;
    			}
    			return result;
    		};
    		zero=(zero+count(0))%mod;
    		one=(one+count(1))%mod;
    	}
    	resultAnd=(resultAnd+(int64_t)one*(1ll<<bit)%mod)%mod;
    	const auto pow2=[](int64_t x) {
    		x%=mod;
    		return x*x%mod;
    	};
    	int_t least = (pow2((int64_t)n*(n+1)/2) - zero + mod) % mod;
    	resultOr=((int64_t)resultOr+least*(1ll<<bit)%mod)%mod;
    }
    namespace SimpleIO{
        const int_t LARGE = 5e7;
        char buf[LARGE + 1];
        char* used = buf;
        void init(){
            fread(buf,1,LARGE,stdin);
        }
        template<class T>
        void nextInt(T& x){
            x = 0;
            while(isdigit(*used) == false) used++;
            while(isdigit(*used)){
                x = x * 10 + (*used) - '0';
                used++;
            }
        }
    }
    int main() {
    //	scanf("%d",&n);
        SimpleIO::init();
        SimpleIO::nextInt(n);
    	int_t maxval=0;
    	for(int_t i=1; i<=n; i++) {
    		for(int_t j=1; j<=n; j++) {
    //			scanf("%d",&mat[i][j]);
                SimpleIO::nextInt(mat[i][j]);
    			maxval=std::max(maxval,mat[i][j]);
    		}
    	}
    	for(int_t i=0; (1ll<<i)<=maxval; i++) {
    		for(int_t j=1; j<=n; j++) {
    			for(int_t k=1; k<=n; k++) {
    				count[j][k]=(mat[j][k]&(1ll<<i))!=0;
    			}
    		}
    		countFor(i);
    	}
    	cout << resultAnd << " " << resultOr << endl;
    	return 0;
    }

     

  • GZOI2019 逼死强迫症

    $$ \text{设}f\left( n \right) \text{表示}2\times n\text{的矩形的方案数。} \\ \text{设}g\left( n \right) \text{为}2\times n,\text{全部使用}1\times \text{2的砖块填充的方案数} \\ \text{转移则有}f\left( n \right) =f\left( n-1 \right) +f\left( n-2 \right) +2\sum_{1\le j\le n-2}{g\left( j-1 \right)}\left( \text{枚举在第}j\text{列放置第一个}1\times \text{1的块,第}n\text{列放置第二个} \right) \\ =f\left( n-1 \right) +f\left( n-2 \right) +\sum_{0\le j\le n-3}{g\left( j \right)} \\ \text{由}g\left( 0 \right) =g\left( 1 \right) =\text{1,}g\left( n \right) =g\left( n-1 \right) +g\left( n-2 \right) \text{知}\sum_{0\le j\le n-3}{g\left( j \right)}=g\left( n-1 \right) -1 \\ \text{故}f\left( n \right) =f\left( n-1 \right) +f\left( n-2 \right) +2g\left( n-1 \right) -2 \\ \,\,\text{构造矩阵} \\ M_1=\left[ \begin{matrix}{l} f\left( 1 \right)& f\left( 2 \right)& g\left( 1 \right)& g\left( 2 \right)& -2\\ \end{matrix} \right] \text{和转移矩阵}T=\left[ \begin{matrix}{l} 0& 1& 0& 0& 0\\ 1& 1& 0& 0& 0\\ 0& 0& 0& 1& 0\\ 0& 2& 1& 1& 0\\ 0& 1& 0& 0& 1\\ \end{matrix} \right] \\ \text{则}M_1T^{n-1}\text{即为结果矩阵。} \\ f\left( 1 \right) =f\left( 2 \right) =\text{0,}g\left( 1 \right) =\text{1,}g\left( 2 \right) =2 \\ $$

    #include <cstring>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1e9 + 7;
    
    struct Matrix {
        int_t data[6][6];
        int_t n;
        Matrix(int_t n) : n(n) { memset(data, 0, sizeof(data)); }
        Matrix operator*(const Matrix& rhs) const {
            Matrix result(n);
            for (int_t i = 1; i <= n; i++) {
                for (int_t j = 1; j <= n; j++) {
                    for (int_t k = 1; k <= n; k++) {
                        result.data[i][j] = (result.data[i][j] +
                                             data[i][k] * rhs.data[k][j] % mod) %
                                            mod;
                    }
                }
            }
            return result;
        }
        Matrix pow(int_t index) {
            Matrix result(n), base = *this;
            for (int_t i = 1; i <= n; i++) result.data[i][i] = 1;
            while (index) {
                if (index & 1) result = result * base;
                base = base * base;
                index >>= 1;
            }
            return result;
        }
    };
    
    int main() {
        int_t T;
        cin >> T;
        Matrix trans(5);
        trans.data[1][2] = 1;
        trans.data[2][1] = 1;
        trans.data[2][2] = 1;
        trans.data[3][4] = 1;
        trans.data[4][2] = 2;
        trans.data[4][3] = 1;
        trans.data[4][4] = 1;
        trans.data[5][2] = 1;
        trans.data[5][5] = 1;
        Matrix M0(5);
        M0.data[1][3] = 1;
        M0.data[1][4] = 2;
        M0.data[1][5] = mod - 2;
        while (T--) {
            int_t n;
            cin >> n;
            if (n == 0)
                cout << 0 << endl;
            else {
                auto res = M0 * trans.pow(n - 1);
                cout << res.data[1][1] << endl;
            }
        }
        return 0;
    }

     

  • CF1150D Three Religions

    完了我已经掉成Pupil了..

    考虑对于每次询问做一个DP处理。

    设$f(i,j,k)$表示使得串1的前i位,串2的前j位,串2的前k位能在主串中同时不相交出现的最短的前缀长度。

    设$g(n,ch)$表示第n个字符之后,ch第一次出现的位置,如果不存在则设为$\infty$。

    转移时考虑$f(i,j,k)=min(g([i\geq 1]f(i-1,j,k),S_1(i)),g([j\geq 1]f(i,j-1,k),S_2(j)),g([k\geq 1]f(i,j,k-1),S_3(k)))$。

    边界条件$f(0,0,0)=0$。

    可是这样单组询问复杂度是$O(250^3)$的,尽管这也是个常数。

    但是注意到每组询问要么是追加一个字符,要么是删除末尾的字符。

    假设在第i个串后追加一个字符,那么只需要把第i个串的DP数组推进一维即可。

    删除第i个串末尾的字符,只需要把DP数组其他两维置INF。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <cstring>
    #include <assert.h>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    const int_t INF = 0x7fffffff;
    const int_t LARGE = 250;
    //µÚn¸ö×Ö·ûºó£¬×Ö·ûcµÚÒ»´Î³öÏÖµÄλÖÃ
    int first[100001][26];
    int dp[251][251][251];
    int pos[4];
    int strs[4][252];
    char str[100010];
    int n,m;
    int at(int pos,char chr) {
    #ifdef DEBUG
    	cout<<"at "<<pos<<" "<<chr<<" = ";
    #endif
    	if(pos>n) {
    #ifdef DEBUG
    		cout<<0x3f3f3f3f<<endl;
    #endif
    		return 0x3f3f3f3f;
    	}
    #ifdef DEBUG
    	cout<<first[pos][chr-'a']<<endl;
    #endif
    	return first[pos][chr-'a'];
    }
    int main() {
    //    cin >> n >> m;
    	scanf("%d%d%s",&n,&m,str + 1);
    	memset(first[n],0x3f,sizeof(first[n]));
    //	cout<<first[n][1]<<endl;
    	for(int_t i = n; i >= 1; i --) {
    		memcpy(first[i-1],first[i],sizeof(first[i]));
    		first[i-1][str[i]-'a']=i;
    
    	}
    #ifdef DEBUG
    	for(int i=0; i<n; i++) for(char chr='a'; chr<='d'; chr++) cout<<"first "<<i<<" "<<chr<<" = "<<first[i][chr-'a']<<endl;
    
    #endif
    	memset(dp,0x3f,sizeof(dp));
    	dp[0][0][0]=0;
    	for(int i=1; i<=m; i++) {
    		static char opt[3];
    		int id;
    		scanf("%s%d",opt,&id);
    		if(opt[0]=='+') {
    			char chr,strx[3];
    			scanf("%s",&strx);
    			chr=strx[0];
    			strs[id][++strs[id][0]]=chr;
    			int len=strs[id][0];
    			if(id==1) {
    //				for(int i=0; i<=strs[1][0]; i++)
    				{
    					int i=strs[1][0];
    					for(int j=0; j<=strs[2][0]; j++) {
    						for(int k=0; k<=strs[3][0]; k++) {
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			} else if(id==2) {
    				for(int i=0; i<=strs[1][0]; i++) {
    //				for(int j=0; j<=strs[2][0]; j++)
    					int j=strs[2][0];
    					{
    						for(int k=0; k<=strs[3][0]; k++) {
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			} else {
    				for(int i=0; i<=strs[1][0]; i++) {
    					for(int j=0; j<=strs[2][0]; j++) {
    						int k=strs[3][0];
    //					for(int k=0; k<=strs[3][0]; k++)
    						{
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			}
    		} else {
    
    			if(id==1) {
    				for(int j=0; j<=strs[2][0]; j++)
    					for(int k=0; k<=strs[3][0]; k++) {
    						dp[strs[id][0]][j][k]=INF;
    					}
    			} else if(id==2) {
    				for(int i=0; i<=strs[1][0]; i++)
    //                for(int j=1;j<=strs[2][0];j++)
    					for(int k=0; k<=strs[3][0]; k++) {
    						dp[i][strs[2][0]][k]=INF;
    					}
    
    			} else {
    				for(int i=0; i<=strs[1][0]; i++)
    					for(int j=0; j<=strs[2][0]; j++) {
    //			        for(int k=1;k<=strs[3][0];k++){
    						dp[i][j][strs[3][0]]=INF;
    					}
    
    			}
    			strs[id][0]--;
    		}
    		if(dp[strs[1][0]][strs[2][0]][strs[3][0]]<=n) {
    			printf("yes\n");
    		} else {
    			printf("no\n");
    		}
    	}
    	return 0;
    }