标签: CCPC

  • CFGYM102394L CCPC2019哈尔滨 L题 LRU Algorithm

    首先做法很简单:令缓存大小为n,然后直接把操作模拟一遍,后期如果我们限制了缓存大小为x,那就等价于取我们在模拟时长度为x的前缀。

    然后我们显然可以把前缀哈希算一算,插到`std::unordered_map`里,然后成功TLE。

    然后我们考虑另一个做法:把询问插到字典树里,仍然对操作进行模拟,每模拟一次后,在字典树上把序列走一遍,并标记对应的询问为Yes。

    但是有几个地方要注意

    • 一个点可能对应多个询问,所以用一个vector来挂询问吧。
    • 一组询问在去除后缀0之后可能长度为0,此时询问是被挂在根上的,务必进行处理,否则WA!
    #include <assert.h>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <unordered_map>
    #include <vector>
    using int_t = int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    using map_t = std::unordered_map<int_t, struct Node*>;
    const int_t LARGE = 5e3 + 20;
    char inputbuf[(int64_t)1e8];
    char* head = inputbuf;
    void initinput() {
        fread(inputbuf, 1, sizeof(inputbuf), stdin);
    }
    char nextchar() {
        assert(head <= inputbuf + sizeof(inputbuf));
        return *(head++);
    }
    template <class T>
    void read(T& x) {
        x = 0;
        char chr = nextchar();
        while (chr < '0' || chr > '9')
            chr = nextchar();
        while (chr >= '0' && chr <= '9') {
            x = x * 10 + chr - '0';
            chr = nextchar();
        }
    }
    
    struct Node {
        std::vector<bool*> result;
        map_t chd;
        ~Node() {
            for (const auto& kvp : chd)
                delete kvp.second;
        }
    };
    
    int_t arr[LARGE + 1];
    int_t arr1[LARGE + 10], queue[LARGE + 1];
    bool result[LARGE + 1];
    int main() {
        initinput();
        int_t T;
        read(T);
        while (T--) {
            queue[0] = 0;
            int_t n, q;
            read(n), read(q);
            Node* root = new Node;
            for (int_t i = 1; i <= n; i++) {
                read(arr[i]);
            }
            for (int_t i = 1; i <= q; i++) {
                result[i] = false;
                Node* curr = root;
                int_t len;
                read(len);
    #ifdef DEBUG
                cout << "insert len " << len << endl;
    #endif
                for (int_t j = 1; j <= len; j++) {
                    int_t x;
                    read(x);
                    if (x == 0)
                        continue;
    #ifdef DEBUG
                    cout << "insert " << x << endl;
    #endif
                    auto& ref = curr->chd[x];
                    if (ref == nullptr)
                        ref = new Node;
                    curr = ref;
                }
                curr->result.push_back(&result[i]);
    #ifdef DEBUG
                cout << "insert ok, result to " << i << endl;
    #endif
            }
            for (int_t i = 1; i <= n; i++) {
                int_t x = arr[i];
                arr1[0] = 0;
                arr1[++arr1[0]] = x;
                for (int_t j = 1; j <= queue[0]; j++) {
                    if (queue[j] != x)
                        arr1[++arr1[0]] = queue[j];
                }
                // arr1[0] = std::min(arr1[0], n);
                assert(arr1[0] <= n);
                memcpy(queue, arr1, sizeof(arr1[0]) * (n + 1));
                Node* curr = root;
    #ifdef DEBUG
                cout << "mapping seq ";
                for (int_t i = 1; i <= queue[0]; i++)
                    cout << queue[i] << " ";
                cout << endl;
    #endif
                for (int_t i = 1; i <= queue[0]; i++) {
                    if (!curr->chd.count(queue[i]))
                        break;
                    else
                        curr = curr->chd[queue[i]];
    #ifdef DEBUG
                    cout << "walk with " << queue[i] << endl;
    #endif
                    for (auto ptr : curr->result) {
                        *ptr = true;
    #ifdef DEBUG
                        cout << "mark result " << (ptr - result) << " to true"
                             << endl;
    #endif
                    }
                }
            }
            for (auto x : root->result)
                *x = true;
            for (int_t i = 1; i <= q; i++) {
                if (result[i]) {
                    puts("Yes");
                } else {
                    puts("No");
                }
            }
            delete root;
        }
        return 0;
    }

     

  • CFGYM102394E CCPC2019哈尔滨 E题 Exchanging Gifts

    假设我们能维护出最终序列的长度L和最终序列出现最多的数的个数cnt,假设$cnt\leq\frac L 2$ ,那么答案是L,这时候我们把序列升序和降序对起来就构造出n的结果。
    假设$cnt>\frac L 2$,那么答案是$2(L-cnt)$,我们仍然把序列升序和降序对应起来,答案是$L-(cnt-(L-cnt))=2*(L-cnt)$
    比如
    “`
    1 2 3 3 3 3 3
    3 3 3 3 3 2 1
    “`
    中间有3个3是重了的,那么重的部分有cnt-(L-cnt)个,其中L-cnt表示的是`1 2`的长度,所以总答案是L-(cnt-(L-cnt))
    现在的问题在于如何维护出这个众数,并且判定$cnt\leq \frac L 2$是否成立。

     

    考虑一种线性时间求求序列众数(出现次数大于一半)的方法:
    令f(i)表示我们从头开始扫到第i个元素的时候(用$a_i$表示),序列中出现次数最多的数与其他的数出现次数之和的差值。同时我们需要维护这个数是多少(用x来表示)。
    每次扫到$a_i$的时候:
    – 如果$a_i=x$,那么f(i)=f(i-1)+1,x不变
    – 如果$a_i\neq x$,那么f(i)=f(i-1)-1,然后如果$f(i)<0$,那么x变为$a_i$,同时$f(i)$取反。

     

    对于这个题,如果我们要使用这种求众数的方法,核心在于如何考虑操作2(合并两个序列时)如何处理。
    我们维护每个序列的f和x,合并两个序列的时候:
    – 如果他们的f相同,那么新序列的f不变,x相加。
    – 如果他们的f不同,那么考虑下两个序列的哪一个的x比较大。如果他们相同,那么新序列的f就写为0(这时候可能存在两个众数),x从原来的x里随便选一个。如果他们不同,那x选择为f较大的那一个,同时f设置为较大值减掉较小值。

     

    所以对于这个题,我们先用这种求众数的方法算出来最终序列的众数。
    但此时求出来的众数,仅仅是在保证`存在一个出现次数超过序列长度一半时候`的众数,如果不存在这样子的众数,也会得出来一个结果,但是并不具有意义。
    所以我们再跑一遍递推,求出来我们上一步求的众数的出现次数,然后我们就可以照着前文所述来算答案了。
    *此外,这个题卡常*
    我跑了半天性能分析后大概知道了几个卡常的点:
    1. 输入量非常巨大,请考虑一次性读入输入数据后自行用缓冲区处理输入。
    2. `std::vector`的构造函数非常慢(性能分析显示,$10^6$次调用大约花了80ms),所以不要使用vector来存储变长的序列,考虑自行分配-回收内存。
    3. `State`结构体的构造函数占了大概40ms的时间,考虑强制内联。
    另外,读入函数千万不要写错了!不要写成`chr>=’9’`!
    #pragma GCC optimize("O2")
    #include <assert.h>
    #include <inttypes.h>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    
    
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 1e6;
    char inputbuf[(int(2e8) / 1024) * 1024];
    char* head = inputbuf;
    inline char nextchar() {
        return *(head++);
    }
    void initinput() {
        fread(inputbuf, 1024, sizeof(inputbuf) / 1024, stdin);
    }
    struct State {
        int64_t len;
        int_t mostval;
        int64_t mostcount;
        inline State(int64_t len = 0, int_t mostval = 0, int64_t mostcount = 0)
            : len(len), mostval(mostval), mostcount(mostcount) {}
        State operator+(const State& rhs) const {
            State result(len + rhs.len, 0, 0);
            if (mostval == rhs.mostval)
                result.mostval = mostval,
                result.mostcount = mostcount + rhs.mostcount;
            else {
                if (mostcount > rhs.mostcount) {
                    result.mostcount = mostcount - rhs.mostcount;
                    result.mostval = mostval;
                } else {
                    result.mostcount = rhs.mostcount - mostcount;
                    result.mostval = rhs.mostval;
                }
            }
            return result;
        }
    };
    std::ostream& operator<<(std::ostream& os, const State& state) {
        os << "State{len=" << state.len << ",mostval=" << state.mostval
           << ",mostcount=" << state.mostcount << "}";
        return os;
    }
    struct Opt {
        int_t type;
        int_t* data;
        int_t datalen;
        int_t x1, x2;
        int64_t mostcount = 0;
    } opts[LARGE + 1];
    State dp[LARGE + 1];
    int_t n;
    template <class T>
    void read(T& x) {
        x = 0;
        char chr = nextchar();
        while (chr < '0' || chr > '9')
            chr = nextchar();
        while (chr >= '0' && chr <= '9') {
            x = x * 10 + chr - '0';
            chr = nextchar();
        }
        assert(x >= 0);
    }
    template <class T>
    void write(T x) {
        assert(x >= 0);
        if (x > 9)
            write(x / 10);
        putchar('0' + x % 10);
    }
    int main() {
        // freopen("input.txt", "r", stdin);
        initinput();
        int_t T;
        read(T);
        while (T--) {
            read(n);
            for (int_t i = 1; i <= n; i++) {
                auto& ref = opts[i];
                if (ref.data) {
                    delete[] ref.data;
                    ref.data = nullptr;
                }
                dp[i] = State();
                read(ref.type);
                if (ref.type == 1) {
                    int_t k;
                    read(k);
                    int_t sum = 0, val = 0;
                    ref.data = new int_t[k + 1];
                    ref.datalen = k;
                    for (int_t i = 1; i <= k; i++) {
                        int_t x;
                        read(x);
                        ref.data[i] = x;
                        if (x == val)
                            sum++;
                        else
                            sum--;
                        if (sum < 0) {
                            sum *= -1, val = x;
                        }
                    }
                    // ref.seq.shrink_to_fit();
                    dp[i] = State(k, val, sum);
                } else {
                    read(ref.x1);
                    read(ref.x2);
                }
            }
            for (int_t i = 1; i <= n; i++) {
                const auto& ref1 = opts[i];
                if (ref1.type == 2) {
                    dp[i] = dp[ref1.x1] + dp[ref1.x2];
                }
            }
    #ifdef DEBUG
            for (int_t i = 1; i <= n; i++) {
                cout << "dp " << i << " = " << dp[i] << endl;
            }
    #endif
            int_t mostval = dp[n].mostval;
            for (int_t i = 1; i <= n; i++) {
                auto& ref = opts[i];
                if (ref.type == 1)
                    ref.mostcount = std::count(ref.data + 1,
                                               ref.data + 1 + ref.datalen, mostval);
                else
                    ref.mostcount = opts[ref.x1].mostcount + opts[ref.x2].mostcount;
            }
            int64_t mostcount = opts[n].mostcount;
    #ifdef DEBUG
            cout << "final len " << dp[n].len << endl;
            cout << "mostcount " << mostcount << endl;
            cout << "mostval " << mostval << endl;
    #endif
            if (mostcount * 2 <= dp[n].len) {
                write(dp[n].len);
            } else {
                write(2 * (dp[n].len - mostcount));
            }
            puts("");
        }
        return 0;
    }

     

  • CFGYM102394I CCPC2019哈尔滨 I题 Interesting Permutations

    签到题我都不会做,我爬了。

    首先检测一些明显非法的情况:

    1. $h_i\geq n$,明显不可能,$h_i$上限是$n-1$

    2. $h_i<h_{i-1}$(对于$i\geq 2$),最大值不会减小,最小值不会增大,所以$h_i$一定是单调不减的。

    3. $h_1\neq 0$,这很显然。

    4. $h_n\neq n-1$这也很显然。

    然后我们考虑从第$2$个元素开始枚举,我们维护一个`gap`变量,用以存储”在当前这个位置,一共有多少个位置可以填数,并且保证填了数之后不影响当前位置前缀最大值和前缀最小值的取值”。当前枚举到第$i$个元素时:

    • 如果$h_i=h_{i-1}$,那就说明当前这个位置的值没有改变前缀最值的分布,那么总答案就可以乘上`gap`(因为有这么多的方案数让我们来填,并且填了后不影响前缀最值),并且 `gap`要减掉1,因为我们填了个数后占了一个空位。
    • 如果$h_i>h_{i-1}$,那就说明当前这个位置的值改变了前缀最大值或者前缀最小值二者之一,那么总答案乘上2。同时`gap`要加上$h_i-h_{i-1}-1$,因为我们新创造了这么多的空位。
    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    
    using int_t = long long;
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1e9 + 7;
    const int_t LARGE = 1e5 + 10;
    int_t arr[LARGE + 1];
    int_t n;
    int main() {
        std::ios::sync_with_stdio(false);
        int_t T;
        cin >> T;
        while (T--) {
            cin >> n;
            bool fail = false;
            for (int_t i = 1; i <= n; i++) {
                cin >> arr[i];
                if (arr[i] < arr[i - 1])
                    fail = true;
                if (i == 1 && arr[i] != 0)
                    fail = true;
                if (i == n && arr[i] != n - 1)
                    fail = true;
                if (arr[i] >= n)
                    fail = true;
            }
            if (fail) {
                cout << 0 << endl;
                continue;
            }
            int_t prod = 1;
            int_t sec = 0;
            for (int_t i = 2; i <= n; i++) {
                if (arr[i] == arr[i - 1]) {
                    prod = prod * sec % mod;
                    sec--;  //消耗了一个中间空位
                }
                if (arr[i] > arr[i - 1]) {
                    prod = prod * 2 % mod;
                    sec = (sec + arr[i] - arr[i - 1] - 1 + mod) % mod;
                }
            }
            cout << prod << endl;
        }
        return 0;
    }

     

     

  • CFGYM 102823 CCPC2018 桂林 B题 Array Modify

    题倒是还行,达标找规律找出来结论,然后就想到了一个多项式快速幂的做法。

    一开始写的两个log的快速幂,TLE。

    然后尝试改成exp+log,跑得看起来快了,但是遇到多组数据(n的大小递增的时候)就挂掉了?

    仔细调查后发现源于自己一直以来exp函数内一直存在的错误:

    exp内调用了log,log内调用了inv,exp内给G0(用以存储log返回值的数组)预留了upper2n(2*n)的空间,但是inv内却使用了upper2n(3*n+1)的空间!

    (调试方法:数组切换为动态分配并开启内存检查)

    对于只计算exp一次而言,这个错误不会造成影响,但是计算多次的时候由于空间污染会导致错误结果。

    解决方法:在exp内也预留upper2n(3*n)的空间,通过本题。

    另外小测试了一下,两个log的多项式快速幂跑本题极限数据,NTT变换的多项式总长度为“27262976”,而exp+log实现的快速幂,NTT变换的多项式总长度为 19922408,我原本以为因为常数原因,exp+log会跑得更慢,没想到事实并非如此。

     

    // luogu-judger-enable-o2
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int mod = 998244353;
    const int g = 3;
    const int LARGE = 1 << 20;
    int revs[21][LARGE + 1];
    
    int power(int base, int index);
    void transform(int* A, int len, int arg);
    void poly_inv(const int* A, int n, int* result);
    void poly_log(const int* A, int n, int* result);
    void poly_exp(const int* A, int n, int* result);
    
    std::vector transform_count;
    #ifdef TDEBUG
    #define TRANSFORM_DEBUG(n) \
        { transform_count.push_back(n); }
    #else
    #define TRANSFORM_DEBUG(n)
    #endif
    int bitRev(int base, int size2) {
        int result = 0;
        for (int i = 1; i < size2; i++) {
            result |= (base & 1);
            base >>= 1;
            result <<= 1;
        }
        result |= (base & 1);
        return result;
    }
    int upper2n(int x) {
        int result = 1;
        while (result < x)
            result *= 2;
        return result;
    }
    int main() {
        std::ios::sync_with_stdio(false);
    
        for (int i = 0; (1 << i) <= LARGE; i++) {
            for (int j = 0; j < LARGE; j++) {
                revs[i][j] = bitRev(j, i);
            }
        }
        static int F[LARGE + 1], G[LARGE + 1];
        static int arr[LARGE + 1];
        int T;
        cin >> T;
        for (int_t _i = 1; _i <= T; _i++) {
            memset(F, 0, sizeof(F));
            memset(G, 0, sizeof(G));
            memset(arr, 0, sizeof(arr));
            int_t n, L, m;
            cin >> n >> L >> m;
    
            for (int_t i = 1; i <= n; i++)
                cin >> arr[i];
            int size2 = upper2n(4 * (n + 1));
            for (int i = 0; i < size2; i++) {
                if (i < L)
                    F[i] = 1;
                else
                    F[i] = 0;
                G[i] = 0;
            }
    #ifdef DEBUG
            cout << "init = " << endl;
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
    
            poly_log(F, n, G);
    #ifdef DEBUG
            cout << "after log = " << endl;
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
            for (int i = 0; i < n; i++)
                G[i] = G[i] * m % mod;
            for (int i = 0; i < size2; i++)
                F[i] = 0;
    #ifdef DEBUG
            cout << "before exp = " << endl;
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
            poly_exp(G, n, F);
    #ifdef DEBUG
            cout << "after exp = " << endl;
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    #endif
    
            for (int i = 0; i < size2; i++) {
                if (i >= n)
                    F[i] = 0;
                if (i >= n && i < 2 * n)
                    G[i] = arr[i - n + 1];
                else
                    G[i] = 0;
            }
            std::reverse(G + n, G + 2 * n);
    
    #ifdef DEBUG
            cout << "F = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << F[i] << " ";
            cout << endl;
    
            cout << "G = ";
            for (int_t i = 0; i < 2 * n; i++)
                cout << G[i] << " ";
            cout << endl;
    #endif
    
            transform(F, size2, 1);
            transform(G, size2, 1);
            for (int i = 0; i < size2; i++)
                F[i] = (int_t)F[i] * G[i] % mod;
            transform(F, size2, -1);
            int inv = power(size2, -1);
            std::reverse(F + n, F + 2 * n);
            cout << "Case " << _i << ": ";
    
            for (int i = n; i < 2 * n; i++) {
                cout << (int_t)F[i] * inv % mod << " ";
            }
            cout << endl;
    #ifdef DEBUG
            for (int i = 0; i < n; i++) {
                cout << "F " << i << " = " << F[i] << endl;
            }
    #endif
    #ifdef TDEBUG
            for (auto x : transform_count) {
                cout << x << " ";
            }
            cout << endl;
    #endif
        }
        return 0;
    }
    
    int power(int base, int index) {
        const int phi = mod - 1;
        index = (index % phi + phi) % phi;
        base = (base % mod + mod) % mod;
        int result = 1;
        while (index) {
            if (index & 1)
                result = (int_t)result * base % mod;
            base = (int_t)base * base % mod;
            index >>= 1;
        }
        return result;
    }
    void transform(int* A, int len, int arg) {
        TRANSFORM_DEBUG(len);
        int size2 = int(log2(len) + 0.5);
        for (int i = 0; i < len; i++) {
            int x = revs[size2][i];
            if (x > i)
                std::swap(A[i], A[x]);
        }
        for (int i = 2; i <= len; i *= 2) {
            int mr = power(g, (int_t)arg * (mod - 1) / i);
            for (int j = 0; j < len; j += i) {
                int curr = 1;
                for (int k = 0; k < i / 2; k++) {
                    int u = A[j + k];
                    int t = (int_t)A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (u - t + mod) % mod;
                    curr = (int_t)curr * mr % mod;
                }
            }
        }
    }
    //计算多项式A在模x^n下的逆元
    // C(x)<-2B(x)-A(x)B^2(x)
    void poly_inv(const int* A, int n, int* result) {
        if (n == 1) {
            result[0] = power(A[0], -1);
            return;
        }
        poly_inv(A, n / 2 + n % 2, result);
        static int temp[LARGE + 1];
        int size2 = upper2n(3 * n + 1);
        for (int i = 0; i < size2; i++) {
            if (i < n)
                temp[i] = A[i];
            else
                temp[i] = 0;
        }
        transform(temp, size2, 1);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++) {
            result[i] =
                ((int_t)2 * result[i] % mod -
                 (int_t)temp[i] * result[i] % mod * result[i] % mod + 2 * mod) %
                mod;
        }
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++) {
            if (i < n) {
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            } else {
                result[i] = 0;
            }
        }
    }
    void poly_log(const int* A, int n, int* result) {
        int size2 = upper2n(3 * n + 1);
        static int Ad[LARGE + 1];
        for (int i = 0; i < size2; i++) {
            if (i < n) {
                Ad[i] = (int_t)(i + 1) * A[i + 1] % mod;
            } else {
                Ad[i] = 0;
            }
            result[i] = 0;
        }
        transform(Ad, size2, 1);
        poly_inv(A, n, result);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++) {
            result[i] = (int_t)result[i] * Ad[i] % mod;
        }
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++)
            if (i < n)
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            else
                result[i] = 0;
        for (int i = n - 1; i >= 1; i--) {
            result[i] = (int_t)result[i - 1] * power(i, -1) % mod;
        }
        result[0] = 0;
    }
    void poly_exp(const int* A, int n, int* result) {
        if (n == 1) {
            assert(A[0] == 0);
            result[0] = 1;
            return;
        }
        poly_exp(A, n / 2 + n % 2, result);
        static int Ax[LARGE + 1];
        // memset(G0, 0, sizeof(G0));
        // memset(Ax,0,sizeof(Ax));
        int size2 = upper2n(3 * n + 1);
        // int* G0 = new int[size2];
        static int G0[LARGE + 1];
        for (int_t i = 0; i < size2; i++) {
            if (i < n)
                Ax[i] = A[i];
            else
                Ax[i] = 0;
            G0[i] = 0;
        }
        poly_log(result, n, G0);
    
        transform(Ax, size2, 1);
        transform(G0, size2, 1);
        transform(result, size2, 1);
        for (int i = 0; i < size2; i++)
            result[i] =
                (result[i] - (int_t)result[i] * (G0[i] - Ax[i] + mod) % mod + mod) %
                mod;
        transform(result, size2, -1);
        for (int i = 0; i < size2; i++)
            if (i < n)
                result[i] = (int_t)result[i] * power(size2, -1) % mod;
            else
                result[i] = 0;
        // delete[] G0;
    }