Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) 简单笔记

好久没打,写不动div1,简单记录一下。

A

按题意暴力做

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 1005;
const ll P = 998244353LL;

int T, n, a[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline bool chk() {
    for (int i = 1; i <= n; i++)
        if (a[i] != i)
            return 0;
    return 1;
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(T);
    for (; T--; ) {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        int ans = 0;
        if (chk()) {
            puts("0");
            continue;
        }
        for (ans = 1; ; ans++) {
            if (ans & 1) {
                for (int i = 1; i < n; i += 2)
                    if (a[i] > a[i + 1])
                        swap(a[i], a[i + 1]);
            } else {
                for (int i = 2; i < n; i += 2)
                    if (a[i] > a[i + 1])
                        swap(a[i], a[i + 1]);
            }
            if (chk()) break;
        }
        printf("%d\n", ans);
    }


#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

B

相当于求一个\(01010101\cdots\)交替出现的串和一个使用了\(a\)\(0\)\(b\)\(1\)的串不同的位数有多少。

最大值应该是先尽量填不同的位数,然后还剩一种颜色全部填下去;最小值类似。求出最大值\(mx\)和最小值\(mn\)之后,发现中间相隔\(2\)的部分全部能取到,因为交换两个数可能产生的贡献为\(0, 2, -2\)

注意到\(n = a + b\)可能会是奇数,所以把上面的串取个反再做一遍。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 2e5 + 5;
const ll P = 998244353LL;

int T, n, a, b, bit[N], cur[N];
bool flag[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline int getMx() {
    int ca = a, cb = b;
    for (int i = 1; i <= n; i++) {
        if (bit[i] == 0) {
            if (cb) --cb, cur[i] = 1;
            else --ca, cur[i] = 0;
        } else {
            if (ca) --ca, cur[i] = 0;
            else --cb, cur[i] = 1;
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i++) res += bit[i] ^ cur[i];
    return res;        
}

inline int getMn() {
    int ca = a, cb = b;
    for (int i = 1; i <= n; i++) {
        if (bit[i] == 0) {
            if (ca) --ca, cur[i] = 0;
            else --cb, cur[i] = 1;
        } else {
            if (cb) --cb, cur[i] = 1;
            else --ca, cur[i] = 0;
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i++) res += bit[i] ^ cur[i];
    return res; 
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(T);
    for (; T--; ) {
        read(a), read(b);
        n = a + b;
        for (int i = 0; i <= n; i++) flag[i] = 0;
        for (int i = 1; i <= n; i++) bit[i] = i & 1;
        int mx = getMx(), mn = getMn();
        assert(mn <= mx);
        assert((mx - mn) % 2 == 0);
        for (int i = mn; i <= mx; i += 2) flag[i] = 1;
        for (int i = 1; i <= n; i++) bit[i] ^= 1;
        mx = getMx(), mn = getMn();
        assert(mn <= mx);
        assert((mx - mn) % 2 == 0);
        for (int i = mn; i <= mx; i += 2) flag[i] = 1;
        vector <int> ans;
        for (int i = 0; i <= n; i++)
            if (flag[i])
                ans.emplace_back(i);
        printf("%d\n", ans.size());
        for (int i = 0; i < ans.size(); i++)
            printf("%d%c", ans[i], " \n"[i == ans.size() - 1]);
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

C

发现答案具有单调性,于是二分\(t\)

观察每一个进入的\(cave\),假设进去时能量为\(t\),那么若能出来,要求\(\forall j \leq k_i, t + j - 1 > a_{i, j}\),移项一下\(t > a_{i, j} + 1 - j\),那么对于\(cave_i\),记录\(b_i = \max(a_{i, j} + 1 - j) (\geq 1)\),进去时需要保证\(t > b_i\)

然后考虑进去的顺序,按照\(b\)从小到大进去就好了,如果不成功,则其他也一定不成功。

二分时注意\(r\)的边界为\(10^9 + 1\),因为是严格大于。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 1e5 + 5;
const int inf = 1000000000;
const ll P = 998244353LL;

int T, n, cnt[N], b[N];
vector <int> a[N];

struct Node {
    int v, c;

    friend bool operator < (const Node x, const Node y) {
        if (x.v != y.v) return x.v < y.v;
        else return x.c > y.c;
    }

} s[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline bool chk(int mid) {
    int cur = mid;
    for (int i = 1; i <= n; i++) {
        if (cur > s[i].v) {
            cur += s[i].c;
        } else return 0;
    }
    return 1;
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(T);
    for (; T--; ) {
        read(n);
        for (int i = 1; i <= n; i++) {
            read(cnt[i]);
            a[i].clear();
            b[i] = 0;
            for (int cur, j = 1; j <= cnt[i]; j++) {
                read(cur);
                a[i].emplace_back(cur);
                b[i] = max(b[i], cur + 1 - j);
            }
            s[i].v = b[i], s[i].c = cnt[i];
        }
        sort(s + 1, s + 1 + n);
        int l = 1, r = inf + 1, mid, res = inf + 1;
        for (; l <= r; ) {
            mid = (l + r) / 2;
            if (chk(mid)) res = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d\n", res);
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

D1

写出递推式:

\[f(n) = \sum_{i = 1}^{n - 1}f(i) + \sum_{i = 2}^{n}f(\lfloor \frac{n}{i} \rfloor) \]

初值\(f(1) = 1\)

整除分块\(O(n\sqrt{n})\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 4e6 + 5;

ll P, f[N];

inline void inc(ll &x, ll y) {
    x += y;
    if (x >= P) x -= P;
}

inline void sub(ll &x, ll y) {
    x -= y;
    if (x < 0) x += P;
}

inline ll fpow(ll x, ll y) {
    ll res = 1;
    for (x %= P; y > 0; y >>= 1) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline void solve(int n) {
    for (ll l = 1, r = 0; l <= n; l = r + 1) {
        r = min(1LL * n, (n / (n / l)));
        inc(f[n], 1LL * f[n / l] * (r - l + 1) % P); 
    }
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    int n;
    read(n), read(P);
    f[1] = 1;
    ll sum = 1;
    for (int i = 2; i <= n; i++) {
        solve(i);
        inc(f[i], sum);
        inc(sum, f[i]);
    } 
    printf("%lld\n", f[n]);

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

然后做不出D2,就挂机了,因为有\(300\)多个人过,所以没开下面的题,直接进行一个罚坐。

D2

这个D2给这一类问题都提供了通用的解决方法,套路狗居然不会这个套路,大失败。

观察上面那个递推式,设\(S(x)\)表示\(x\)转移过来的\(f\)集合,考虑\(S(x + 1)\)\(S(x)\)有啥差别:

  • \(S(x + 1)\)一定比\(S(x)\)多一个\(f(1)\),因为有一个\(f(\lfloor \frac{x + 1}{x + 1} \rfloor)\)

  • \(S(x + 1)\)一定比\(S(x)\)多一个\(f(x)\)

  • 考虑\(\sum_{i = 2}^{x} f(\lfloor \frac{x}{i} \rfloor)\)\(\sum_{i = 2}^{x} f(\lfloor \frac{x + 1}{i} \rfloor)\)之间有什么差别,这个差别应该很小,因为\(x + 1\)仅仅增加了\(1\)。发现只有当\(i | x + 1\)的时候才有变化,每一个\(f(i - 1)\)\(f(i)\)替换掉了。

前两种变化易于维护,针对最后一种变化,不妨对于每一个已算出的\(f(c)\),将所有\(2c, 3c, \cdots\)位置的\(f\)都增加\(f(c) - f(c - 1)\)。做的时候可以单独做\(f(2)\)\(2 - 1 = 1\)太特殊了。

时间复杂度是一个调和级数\(O(n\log n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 4e6 + 5;

int n;
ll f[N], P;

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline void inc(ll &x, ll y) {
    x += y;
    if (x >= P) x -= P;
}

inline void sub(ll &x, ll y) {
    x -= y;
    if (x < 0) x += P;
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(n), read(P);
    ll sum = 1;
    f[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (i == 2) f[2] = 2;
        else {
            inc(f[i], f[i - 1]);
            inc(f[i], f[i - 1]);
            inc(f[i], 1);    
        }
        for (int j = i + i; j <= n; j += i) {
            inc(f[j], f[i]);
            sub(f[j], f[i - 1]);
        }
        // inc(sum, f[i]);
        // printf("%lld\n", sum);
        // for (int j = 1; j <= n; j++) printf("%lld%c", f[j], " \n"[j == n]);
    }
    // for (int i = 1; i <= n; i++) printf("%lld\n", f[i]);
    printf("%lld\n", f[n]);

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

E

题意:给一个长度为\(n\)的排列(\(n\)为奇数)\(a[1 : n]\),每次可以选择一个奇数位置\(p\)\(a[1 : p]\)翻转,问至多\(\frac{5n}{2}\)次操作能否将其升序排序。

首先考虑不可行的情况,发现按照奇数位置排序之后原来在奇数位置的数还是在奇数位,原来在偶数位置的数还是在偶数位;而最终目标要求奇数都在奇数位,偶数都在偶数位。得出必要条件:所有奇数都在奇数位,偶数都在偶数位。

接下来考虑用构造方法证明这个必要条件充分。发现翻转\(a[1 : p]\)\(p\)之后的位置无关,所以考虑先将\(n\)\(n - 1\)分别放入其所在位置中,然后整个排序过程与最后两个位置无关,前面是一个一样的子问题。观察限制\(\frac{5n}{2}\),感觉用至多\(5\)步将当前最后两个数\(n\)\(n - 1\)归位比较妥当。

接下来手玩构造方法,感觉这个构造并不复杂,直接给方法:

  • 首先找到\(n\)的位置,记为\(p_1\),直接翻转\(a[1 : p_1]\),将\(n\)放到\(1\)的位置。

  • 然后找到\(n - 1\)的位置,记为\(p_2\),翻转\(a[1 : p_2 - 1]\),将\(n\)\(n - 1\)放到一起,此时的局面应该是:
    _ _ _ _ _ _ _ _ ... n (n - 1) _ _ ...

  • 直接翻转\(a[1 : n]\),将\(n - 1\)放到前面,此时的局面是:
    _ _ _ ... (n - 1) n ... _ _ ...

  • 找到此时\(n\)的位置\(p_1\),翻转\(a[1 : p_1]\),将\(n\)放到第一位,将\(n - 1\)放到第二位。

  • 最后翻转\(a[1 : n]\),将\(n\)\(n - 1\)放到序列的最后。

这个构造显然符合条件。

代码偷了很多懒。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 3005;
const ll P = 998244353LL;

int T, n, a[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;  

inline bool chk() {
    for (int i = 1; i <= n; i++)
        if ((a[i] & 1) != (i & 1))
            return 0;
    return 1;
}

inline void doReverse(int pos) {
    for (int i = 1, j = pos; i < j; i++, j--) swap(a[i], a[j]);
}

inline int get(int cur) {
    for (int i = 1; i <= n; i++)
        if (a[i] == cur)
            return i;
    assert(0);
}

inline void solve() {
    int lim = n / 2 * 5;;
    vector <int> res;
    for (; n != 1; n -= 2) {
        int p1 = get(n), p2 = get(n - 1);
        assert(p1 & 1), assert(!(p2 & 1));
        if (p1 == n && p2 == n - 1) continue;
        res.emplace_back(p1);
        doReverse(p1);
        p1 = get(n), p2 = get(n - 1);
        assert(p1 == 1);
        res.emplace_back(p2 - 1);
        doReverse(p2 - 1);
        res.emplace_back(n);
        doReverse(n);
        p1 = get(n), p2 = get(n - 1);
        res.emplace_back(p1);
        doReverse(p1);
        p1 = get(n), p2 = get(n - 1);
        assert(p1 == 1 && p2 == 2);
        res.emplace_back(n);
        doReverse(n);
        p1 = get(n), p2 = get(n - 1);
        assert(p1 == n && p2 == n - 1);
    }
    assert(res.size() <= lim);
    printf("%d\n", res.size());
    for (int i = 0; i < res.size(); i++)
        printf("%d%c", res[i], " \n"[i == res.size() - 1]);
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(T);
    for (; T--; ) {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        if (!chk()) puts("-1");
        else solve();
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

F

题意:

定义了一种插入排序的“插入”操作:用一个二元组\((x, y)\)表示将\(x\)位置的数插入到\(y\)位置前面\((x > y)\),其中\(y\)是第一个满足\(a_y > a_x\)的位置;如果\(a_x \geq a_{x - 1}\),那么插入操作不会发生。

现在给出一个长度为\(n\)的数列和其合法的“插入”操作的顺序,每一个数的取值范围是\([1, n]\)(读了好几遍没说数的取值范围,这个应该是根据样例脑补出来的,事实上也就是这样),数满足条件的序列有多少个。

多组数据,要注意的是\(n\)的总大小是没有限制的,\(\sum m \leq 200000\)\(m\)是给出的“插入”操作的总个数。

分割线

玩一玩样例,发现给出了所有的“插入”之后,所有数的大小关系是确定的,但是中间连接的不等号是不确定的(\(<\)\(\leq\));没有给出插入操作的数,其实也蕴含了大小关系\((a_x \geq a_{x - 1})\)

用第三个样例为例,每步插入之后的大小关系是这样的:

  • \(a_1 \leq a_2\)

  • \(a_3\)插入到第一个数的前面,\(a_3 < a_1 \leq a_2\)

  • \(a_4\)插在最后,\(a_3 < a_1 \leq a_2 \leq a_4\)

  • \(a_5\)插在第三个数的前面,注意到\(a_5\)的右边是\(<\),其左边是\(\leq\)\(a_3 < a_1 \leq a_5 < a_2 \leq a_4\)

然后考虑一下怎么数数,限制条件即为\(1 \leq a_3 < a_1 \leq a_5 < a_2 \leq a_4 \leq 5\),要求\(a[1 : 5]\)的数量。这是一个高考填空题的水平……

具体做法是:

\(x_1 = a_3 \geq 1, x_2 = a_1 - a_3 \geq 0, x_3 = a_5 - a_1 \geq 1, x_4 = a_2 - a_5 \geq 1, x_5 = a_4 - a_2 \geq 0\),求\(x_1 + x_2 + x_3 + x_4 + x_5 = a_5 \leq 5\)的解个数,应该是\(\binom{5 + 2}{5} = 21\)

假设最后的序列中\(\leq\)\(cnt\)个,那么最后的答案就是\(\binom{n + cnt}{n}\)

现在问题变成维护最后的序列中有多少个\(<\)或者是\(\leq\),我们注意到将一个数插入到\(\leq\)的位置(或者是第一个位置)会增加一个\(<\);而将一个数插入到\(<\)的位置不会导致\(<\)的数量变化。也就是说在插入的时候还得知道当前这个位置是个啥符号。考虑将这个序列直接维护出来,由于我个人做这样的问题实在是很少,所以接下来的打开方式应该是不太对的,导致我的做法和代码都无比复杂。

如果这个\(\sum n\)是有限制的,那么直接暴力开一棵平衡树就可以知道最后每个数的位置了;但是很可惜没有,需要将每一组数据的点数缩减到\(O(m)\),感觉直接离散化离不出来,我的做法是维护一棵结点代表一个区间\([l : r]\)的平衡树,可以发现这样的结点一定是不在输入中给出的,而且中间的权值一定是连续的,如果插入的时候需要插到一个结点的中间,就将这个结点分裂为\([l : l + k], [l + k + 1 : r]\),然后插入,这样也可以知道所有用到的位置。考虑到“分裂”这个操作,所以自己写的时候选择了fhqTreap。得到最后的位置之后然后对着这个处理过后的区间暴力开个线段树维护一下中间连接的符号,最后去数有多少个\(<\)就好了。

时间复杂度应该是\(O(m\log m)\),代码写崴了,应该退化成\(O(m\log^2 m)\)了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 6e5 + 5;
const int Maxn = 6e5;
const ll P = 998244353LL;

int m, tot, pos[N];
bool flag[N];
ll fac[N], ifac[N];

struct Node {
    int l, r, pos;

    friend bool operator < (const Node x, const Node y) {
        return x.l < y.l;
    }

};
vector <Node> finalSeg;

inline void inc(ll &x, ll y) {
    x += y;
    if (x >= P) x -= P;
}

inline void sub(ll &x, ll y) {
    x -= y;
    if (x < 0) x += P;
}

inline ll fpow(ll x, ll y) {
    ll res = 1;
    for (x %= P; y > 0; y >>= 1) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

inline ll binom(int x, int y) {
    if (y > x || x < 0 || y < 0) return 0;
    else return fac[x] * ifac[y] % P * ifac[x - y] % P;
}

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

namespace FhqTreap {
    int rt, nodeCnt, siz[N], keyl[N], keyr[N], pri[N], ch[N][2];

    #define lc ch[p][0]
    #define rc ch[p][1]

    inline void clear() {
        for (int i = 1; i <= nodeCnt; i++) 
            siz[i] = keyl[i] = keyr[i] = pri[i] = ch[i][0] = ch[i][1] = 0; 
        rt = nodeCnt = 0;
    }

    inline int newNode(int l, int r) {
        int t = ++nodeCnt;
        keyl[t] = l, keyr[t] = r, siz[t] = r - l + 1; 
        pri[t] = rand();
        ch[t][0] = ch[t][1] = 0;
        return t;
    }

    inline void up(int p) {
        siz[p] = siz[lc] + siz[rc] + keyr[p] - keyl[p] + 1;
    }

    int merge(int x, int y) {
        if (!x || !y) return x + y;
        if (pri[x] < pri[y]) {
            ch[x][1] = merge(ch[x][1], y);
            up(x);
            return x;
        } else {
            ch[y][0] = merge(x, ch[y][0]);
            up(y);
            return y;
        }
    }

    void split(int p, int k, int &x, int &y) {
        /* !assure k is proper before calling split */
        if (!p) x = y = 0;
        else {
            if (siz[lc] + keyr[p] - keyl[p] + 1 <= k) {
                x = p;
                split(rc, k - (siz[lc] + keyr[p] - keyl[p] + 1), rc, y);
            } else {
                y = p;
                split(lc, k, x, lc);
            }
            up(p);
        }
    }

    inline pair <bool, pin> get(int root, int k) {
        /* 0 , in segment */
        /* 1 , the successor */
        if (k == 0) return make_pair(1, pin(0, 0));
        int p = root, cur = 0;
        for (; p; ) {
            if (cur + siz[lc] + keyr[p] - keyl[p] + 1 == k) {
                return make_pair(1, pin(cur + siz[lc] + keyr[p] - keyl[p] + 1, p));
            } else if (cur + siz[lc] + keyr[p] - keyl[p] + 1 < k) {
                cur += siz[lc] + keyr[p] - keyl[p] + 1;
                p = rc;
            } else if (cur + siz[lc] >= k) {
                p = lc;
            } else {
                return make_pair(0, pin(cur + siz[lc], p));
            }
        }
        // assert(0);
    }

    inline void ins(int k, int l, int r) {
        pair <bool, pin> cur = get(rt, k);
        if (!cur.first) {
            assert(l == r);
            int x, y, z;
            split(rt, cur.second.first, x, y);
            int p = cur.second.second;
            split(y, keyr[p] - keyl[p] + 1, y, z);
            int mid = keyl[p] + k - cur.second.first - 1;
            int t1 = newNode(keyl[p], mid);
            int t2 = newNode(mid + 1, keyr[p]);
            int t3 = newNode(l, r);
            rt = merge(merge(x, merge(t1, merge(t3, t2))), z);
        } else {
            assert(cur.second.first == k);
            int x, y;
            split(rt, k, x, y);
            rt = merge(merge(x, newNode(l, r)), y);
        }
        tot += r - l + 1;
    }

    void dfs(int p) {
        if (!p) return;
        dfs(lc);
        ++m;
        finalSeg.emplace_back((Node) {keyl[p], keyr[p], m});
        dfs(rc);
    }

    void debug(int p) {
        if (!p) return;
        debug(lc);
        printf("[%d, %d] ", keyl[p], keyr[p]);
        debug(rc);
    }

    #undef lc
    #undef rc

}
using FhqTreap::ins;
using FhqTreap::dfs;
using FhqTreap::clear;

namespace SegT {
    bool s[N << 2], ex[N << 2]; 
    /* 0 : <= */
    /* 1 : < */ 

    #define lc (p << 1)
    #define rc (p << 1 | 1)
    #define mid ((l + r) >> 1)

    inline void up(int p) {
        ex[p] = ex[lc] | ex[rc];
    }

    void build(int p, int l, int r) {
        ex[p] = 0;
        if (l == r) {
            s[p] = 0;
            return;
        }
        build(lc, l, mid);
        build(rc, mid + 1, r);
        // up(p);
    }

    void modify(int p, int l, int r, int x, bool v) {
        if (l == r) {
            ex[p] = 1;
            s[p] = v;
            return;
        }
        if (x <= mid) modify(lc, l, mid, x, v);
        else modify(rc, mid + 1, r, x, v);
        up(p);
    }

    bool getEx(int p, int l, int r, int x, int y) {
        if (x <= l && y >= r) return ex[p];
        bool res = 0;
        if (x <= mid) res |= getEx(lc, l, mid, x, y);
        if (y > mid) res |= getEx(rc, mid + 1, r, x, y);
        return res;
    }

    int getPos(int p, int l, int r, int x) {
        if (!ex[p]) return 0;
        if (l == r) return l;
        if (x <= mid) return getPos(lc, l, mid, x);
        else {
            bool cur = getEx(rc, mid + 1, r, mid + 1, x);
            if (cur) return getPos(rc, mid + 1, r, x);
            else return getPos(lc, l, mid, x);
        }
    }

    int query(int p, int l, int r, int x) {
        if (l == r) return s[p] ? 1 : 0;
        if (x <= mid) return query(lc, l, mid, x);
        else return query(rc, mid + 1, r, x);
    }

    inline void debug() {
        for (int i = 1; i <= m; i++)
            printf("%d%c", query(1, 1, m, i), " \n"[i == m]);
    }

    #undef lc
    #undef rc
    #undef mid

}
using SegT::build;
using SegT::modify;
using SegT::query;
using SegT::getPos;

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    // freopen("sample.out", "w", stdout);
    clock_t st_clock = clock();
#endif

    fac[0] = 1;
    for (int i = 1; i <= Maxn; i++) fac[i] = fac[i - 1] * i % P;
    ifac[Maxn] = fpow(fac[Maxn], P - 2);
    for (int i = Maxn - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % P;

    int T;
    read(T);
    for (int n, qn; T--; ) {
        read(n), read(qn);
        vector <pin> vec(qn + 1);
        for (int i = 1; i <= qn; i++) 
            read(vec[i].first), read(vec[i].second);
        tot = 0;
        clear();
        int lst = 1;
        for (int i = 1; i <= qn; i++) {
            if (vec[i].first - 1 >= lst) {
                ins(tot, lst, vec[i].first - 1);

                // FhqTreap::debug(FhqTreap::rt);
                // puts("");
            } 
            ins(vec[i].second - 1, vec[i].first, vec[i].first);
            lst = vec[i].first + 1;

            // FhqTreap::debug(FhqTreap::rt);
            // puts("");
        }
        if (tot != n) ins(tot, lst, n);
        
        // FhqTreap::debug(FhqTreap::rt);
        // puts("");

        if (!finalSeg.empty()) finalSeg.clear();
        dfs(FhqTreap::rt);
        sort(finalSeg.begin(), finalSeg.end());

        build(1, 1, m);
        for (int j = 0, i = 1; i <= qn; i++) {
            for (; j < finalSeg.size() && finalSeg[j].l < vec[i].first; ++j)
                modify(1, 1, m, finalSeg[j].pos, 0);
            assert(finalSeg[j].l == vec[i].first);
            int cur = finalSeg[j].pos;
            int pre = getPos(1, 1, m, cur);
            modify(1, 1, m, cur, 1);
            if (pre) modify(1, 1, m, pre, 0);
            ++j;
            // SegT::debug();
        }
        int cnt = 0;
        for (int i = 1; i < m; i++) cnt += query(1, 1, m, i);
        cnt = n - 1 - cnt;
        
        // printf("%d\n", cnt);
        
        ll ans = binom(n + cnt, n);
        printf("%lld\n", ans);
        m = 0;
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}
posted @ 2021-08-25 09:57  CzxingcHen  阅读(59)  评论(0编辑  收藏  举报