2023.02.13 模拟赛小结

2023.02.13 模拟赛小结

更好的阅读体验戳此进入

赛时思路

T1

CF840C On the Bench

给定序列,求序列有多少个排列满足任意相邻两数之积非完全平方数。

赛时最终想到的是质因数分解后,两数合成之后若所有质因数幂次均为偶数则为完全平方数,依此可以 $ O(n^2 \log^2 a) $ 处理每个数可以(或不可以)和哪些数相邻,这样的话如果求的不是排列而是任意序列那就是一个非常朴素的 DP 了,不过排列就完全没有想到什么复杂度正确的好做法,唯一想到了一个 $ O(n^5) $ 的不知道正确性的区间 DP 做法,即考虑 $ dp(l, r, x, y) $ 表示 $ [l, r] $ 生成的左端点为 $ x $ 右端点为 $ y $ 的排列,然后枚举分割点转移,当然这个东西既不知道正确性又没有部分分,所以直接考虑 next_permutation()。期望 $ 10\mathrm{pts} $。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MOD (ll)(1e9 + 7)
#define LIM (110000)

template < typename T = int >
inline T read(void);

int N;
int A[310];
struct Fact{int val, cnt;};
basic_string < Fact > facts[310];
basic_string < int > legal[310];
bitset < LIM + 100 > notPrime;
basic_string < int > Prime;


int main(){
    freopen("square.in", "r", stdin);
    freopen("square.out", "w", stdout);
    N = read();
    // for(int i = 2; i <= LIM; ++i){
    //     if(!notPrime[i])Prime += i;
    //     for(auto p : Prime){
    //         if((ll)i * p > LIM)break;
    //         notPrime[i * p] = true;
    //         if(i % p == 0)break;
    //     }
    // }
    for(int i = 1; i <= N; ++i){
        int val = A[i] = read();
        // for(auto p : Prime){
        //     if((ll)p * p > val)break;
        //     if(val % p == 0)facts[i] += Fact{p, 0};
        //     while(val % p == 0)facts[i].back().cnt++, val /= p;
        // }if(val != 1)facts[i] += Fact{val, 1};
    }
    int ans(0);
    int base(1);
    for(int i = 1; i <= N; ++i)base *= i;
    for(int i = 1; i <= base; ++i){
        bool flag(true);
        for(int j = 1; j < N; ++j){
            ll val = (ll)A[j] * A[j + 1];
            if((ll)sqrt(val) * (ll)sqrt(val) == val){flag = false; break;}
        }if(flag)++ans;
        next_permutation(A + 1, A + N + 1);
    }printf("%d\n", ans);


    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T2

CF819D Mister B and Astronomers

存在一个星星每 $ T $ 秒闪烁一次,存在 $ n $ 个科学家围成一圈循环观测星星,第一个在 $ 0 $ 秒观测,第二个在 $ a_2 $ 秒观测,第三个在 $ a_2 + a_3 $ 秒观测,$ \cdots $ 第一个科学家在 $ a_1 + a_2 + a_3 + \cdots + a_n $ 观测,$ \cdots $。对于 $ t \in [0, T) $,若星星第一次闪烁在 $ t $ 秒,那么第一次观测到这个星星的科学家将会获得 $ 1 $ 的贡献,求每个科学家的贡献。

一道十分高妙的 $ \operatorname{exgcd} $ 题,赛时大概是发现了会根据同余成环,环数是 $ \gcd(\sum a, T) $,这样的话部分分就是暴力枚举每个 $ t $ 然后一直枚举是否出现与对应科学家同余并计算贡献即可,不难想到若两者互质那么一定有解,而在暴力分中两者不互质的部分可能存在不再同一环内导致无解,这里一个简单的办法就是均分时间然后卡时即可,期望得分 $ 20\mathrm{pts} $。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

int N, T;
int A[210000];
ll sumA(0);
int ans[210000];
unordered_map < ll, int > equiv;

int main(){
    freopen("moon.in", "r", stdin);
    freopen("moon.out", "w", stdout);
    T = read(), N = read();
    for(int i = 1; i <= N; ++i)sumA += (A[i] = read());
    equiv[0] = 1;
    int curs(0);
    for(int i = 2; i <= N; ++i)curs += A[i], equiv[curs] = i;
    double clock_lim = 2.0 / (double)T;
    for(int t = 0; t <= T - 1; ++t){
        double sc = (double)clock() / CLOCKS_PER_SEC;
        int cur(t % sumA);
        while(equiv.find(cur) == equiv.end() && (double)clock() / CLOCKS_PER_SEC - sc <= clock_lim)(cur += T) %= sumA;
        if(equiv.find(cur) != equiv.end())ans[equiv[cur]]++;
    }
    for(int i = 1; i <= N; ++i)printf("%d%c", ans[i], i == N ? '\n' : ' ');

    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T3

没找到原题。

CF793F Julia the snail

大概就是存在一个一维空间,最小位置在 $ 1 $,最大位置为 $ n $,你可以随时减少位置,但只能使用 $ m $ 个单向传送门从 $ l_i $ 到 $ r_i \(,\) q $ 次询问给定限定 $ [x_i, y_i] $ 表示你初始在 $ x_i $,并位置最小不能小于 $ x_i $,最大不能大于 $ y_i $,求最大能到达哪个位置。

部分分给的还行,前 $ 20\mathrm{pts} $ 白送的,后面两个部分分是传送门无重叠和询问无重叠,怎么说呢,想到的几个思路最后都被我卡掉了,理论上满的时候都是 $ m^2 $ 的,不过数据比较弱又多过了 $ 20\mathrm{pts} $。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

int N, M, Q;

// class SegTree{
// private:
//     int cnt[110000 << 2];
//     int sum[110000 << 2];
//     int lz[110000 << 2];
//     #define LS (p << 1)
//     #define RS (LS | 1)
//     #define MID ((gl + gr) >> 1)
// public:
//     void Pushup(int p){
//         sum[p] = sum[LS] + sum[RS];
//         cnt[p] = cnt[LS] + cnt[RS];
//     }
//     void Pushdown(int p){
//         if(!lz[p])return;

//     }
// }

struct Sline{
    int l, r;
    friend const bool operator < (const Sline &a, const Sline &b){
        return a.l == b.l ? a.r < b.r : a.l < b.l;
    }
}s[110000];

int main(){
    freopen("snail.in", "r", stdin);
    freopen("snail.out", "w", stdout);
    N = read(), M = read();
    for(int i = 1; i <= M; ++i)s[i].l = read(), s[i].r = read();
    sort(s + 1, s + M + 1);
    Q = read();
    if((ll)M * Q <= (ll)(1e8)){
        while(Q--){
            int l = read(), r = read();
            int curl = l, curr = l;
            for(int i = 1; i <= M; ++i)
                if(curl <= s[i].l && s[i].l <= curr && s[i].r <= r)curr = max(curr, s[i].r);
            printf("%d\n", curr);
        }
    }else{
        bool spLine(true);
        for(int i = 1; i <= M - 1; ++i)
            if(s[i].l < s[i + 1].l && s[i + 1].l < s[i].r){spLine = false; break;}
        if(spLine){
            basic_string < int > ans[110000];
            for(int i = M; i >= 1; --i){
                ans[s[i].l] += s[i].r;
            }
            for(int i = N; i >= 1; --i){
                basic_string < int > tmp = ans[i];
                for(auto v : tmp)ans[i] += ans[v];
            }
            while(Q--){
                int l = read(), r = read();
                int anss(l);
                for(auto v : ans[l])if(v <= r)anss = max(anss, v);
                printf("%d\n", anss);
                // for(int i = 1; i <= M; ++i)
                //     if(curl <= s[i].l && s[i].l <= curr && s[i].r <= r)curr = max(curr, s[i].r);
                // printf("%d\n", curr);
            }
        }
    }
    

    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

正解

T1

首先有一个更高妙的转换,可以认为 $ 50% $ 的部分分是一个提示,即所有数都是质数的时候问题转化为不能有相同的数相邻,而正解可以先考虑将每个数的平方质因子全部去除,这样问题即可转化为要求相邻两数不同,考虑如何处理。

存在朴素 DP,考虑按序添加数(这个东西怎么处理赛时属实是想了好久假了好久),不难发现添加 $ a_{i + 1} $ 到 $ [1, i] $ 的排列时,有四种情况,即两侧均为 $ a_{i + 1} $,一侧为 $ a_{i + 1} $,两侧相同且均不为 $ a_{i + 1} $,两侧不同且均不为 $ a_{i + 1} $。

显然处理排列时原序列不同顺序本质相同,于是考虑将原序列排序,依次处理 $ s_i $ 表示前 $ i $ 个数里有多少个 $ a_{i + 1} $。

同时令 $ dp(i, j, k) $ 表示考虑前 $ i $ 个数,有 $ j $ 个两侧数相同且不为 $ a_{i + 1} $,有 $ k $ 个两侧数均为 $ a_{i + 1} $,显然对于前两种情况,考虑应有 $ 2 \times s_i $ 个位置,每存在一个相邻两侧均为 $ a_{i + 1} $ 的就会减少 $ 1 $,即转移为:

\[dp(i, j, k) \times (2 \times s_i - k) \rightarrow dp(i + 1, j, k + 1) \]

对于第三种情况,同理:

\[dp(i, j, k) \times j \rightarrow dp(i + 1, j - 1, k) \]

对于第四种情况,同理:

\[dp(i, j, k) \times (i + 1 - (2 \times s_i - k) - j) \rightarrow dp(i + 1, j, k) \]

同时这里我们注意一个问题,当 $ a_i \neq a_{i + 1} $ 时,我们原来记录的 $ k $ 的意义会改变,换句话说原来的 $ k $ 会在新的这一次变为 $ j $,于是此时将所有 $ dp(i, j, k) $ 的贡献转移到 $ dp(i, j + k, 0) $ 即可。

最终答案显然为 $ dp(n, 0, 0) $,复杂度 $ O(n^3) $。

双倍经验 LG-P4448 [AHOI2018初中组]球球的排列

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define LIM (110000)
#define MOD (ll)(1e9 + 7)

template < typename T = int >
inline T read(void);

int N;
ll A[310];
ll s[310];
ll dp[2][310][310];
bitset < LIM + 100 > notPrime;
basic_string < int > Prime;

int main(){
    for(int i = 2; i <= LIM; ++i){
        if(!notPrime[i])Prime += i;
        for(auto p : Prime){
            if((ll)i * p > LIM)break;
            notPrime[i * p] = true;
            if(i % p == 0)break;
        }
    }
    N = read();
    for(int i = 1; i <= N; ++i){
        A[i] = read();
        for(auto p : Prime){
            if((ll)p * p > A[i])break;
            while(A[i] % (ll)(p * p) == 0)A[i] /= (ll)p * p;
        }
    }
    sort(A + 1, A + N + 1);
    int cnt(0);
    for(int i = 1; i < N; ++i)
        if(A[i] != A[i + 1])s[i] = 0, cnt = 0;
        else s[i] = ++cnt;
    dp[0][0][0] = 1; bool cur(0);
    for(int i = 0; i < N; ++i){
        memset(dp[cur ^ 1], 0, sizeof dp[cur ^ 1]);
        if(A[i] != A[i + 1])
            for(int j = 0; j <= N; ++j)
                for(int k = 1; k <= N; ++k)
                    if(j + k <= N)(dp[cur][j + k][0] += dp[cur][j][k]) %= MOD, dp[cur][j][k] = 0;
        for(int j = 0; j <= N; ++j)
            for(int k = 0; k <= N; ++k){
                if(2 * s[i] - k > 0)(dp[cur ^ 1][j][k + 1] += dp[cur][j][k] * (2 * s[i] - k) % MOD) %= MOD;
                if(j - 1 >= 0)(dp[cur ^ 1][j - 1][k] += dp[cur][j][k] * j % MOD) %= MOD;
                if(i + 1 - (2 * s[i] - k) - j >= 0)(dp[cur ^ 1][j][k] += dp[cur][j][k] * (i + 1 - (2 * s[i] - k) - j) % MOD) %= MOD;
            }
        cur ^= 1;
    }printf("%lld\n", dp[cur][0][0]);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T2

首先不难想到,令 $ S = \sum a_i $,第 $ i $ 个科学家观测时间为 $ \sum_{j = 2}^i a_j + kS, k \in \mathbb{N} $,令 $ t_i = \sum_{j = 2}^i a_j $,同时对于 $ T $,假设本次初始时间为 $ \xi $,那么可以认为需要找到第一个满足 $ t_i + kS \equiv \xi \pmod{T} $。

对于后者式子不难发现其为经典的群论套路,即我们发现对于某个科学家的初始点 $ t_i $,可以认为每次模意义下步进 $ S $,此时会构成 $ \gcd(S, T) $ 个环,环长均为 $ \dfrac{T}{\gcd(S, T)} $,或者说每连续 $ \gcd(S, T) $ 个数均属于不同的环。关于这个性质似乎特别显然,不过这里我们也简单证明一下:

显然我们要证明的即为如下式子:

\[\forall i \in [0, T), i \equiv i + \gcd(S, T) \pmod{\gcd(S, T)} \]

\[\forall i \in [0, T), t \in (0, \gcd(S, T)), i + t \not\equiv i \pmod{\gcd(S, T)} \]

证明均显然。

此时我们考虑,对于一个环内的所有科学家,$ \xi $ 为环上某一点的贡献,我们要找到的就是第一个,或者说其减少最少个 $ S $ 遇到的 $ t_i $,这个东西不难想到,对于两个同环科学家 $ t_i, t_j $,且 $ t_j $ 在 $ t_i $ 之后,两者之间模意义下距离了多少个 $ S $ 就代表 $ i $ 的答案由多少。而对于不同环中显然互相无交无关联。

所以我们可以考虑对于一个环内的所有科学家 $ t_1, t_2, \cdots $,我们如果能够按照其在环内的遍历顺序排序,或者说满足 $ t_i $ 之后的 $ t_j $ 一定是 $ t_i $ 经过最小次模意义下加 $ S $ 的操作得到的(这里如何做我们后文再叙),然后遍历一遍,此时对于相邻的 $ t_i \lt t_j $(注意此时的 $ \lt $ 意义是我们重载过的小于号,且两者相邻或 $ i $ 为末尾 $ j $ 为初始)存在:

\[t_i + xS \equiv t_j \pmod{T} \]

转化后得到:

\[Sx + Ty = t_j - t_i \]

并且显然满足 $ \gcd(S, T) \mid t_j - t_i $,满足 exgcd 的限制,注意此时 $ y $ 是任意的,所以仅需求出 $ x $ 的最小非负整数解,其即代表了 $ t_i $ 对应科学家的总贡献,也就是答案。同时注意若环内仅有一个科学家则其贡献直接为 $ \dfrac{T}{\gcd(S, T)} $。

现在我们在将目光移回上文所述的排序过程,考虑发现该限制对应到上述方程的意义就是 $ x $ 最小的解,如果满足 $ S \perp T $ 那么我们是可以通过乘逆元转换为 $ t_i \times S^{-1} \bmod{T} $ 的偏序关系,但本题显然不满足,于是不难想到若其初始时位置为 $ p_i = t_i \bmod{\gcd(S, T)} $,那么一定存在 $ p_i + kS \equiv t_i \pmod{T} $,发现此方程亦为 exgcd 形式,转换为 $ Sx + Ty = t_i - p_i $,求出 $ x $ 的最小非负整数解即可得到其在环中对应的位置,以其为关键字进行排序则一定满足我们上述的偏序关系。而解相同的,即同环模意义下同位置的,不难想到我们优先保留 $ t_i $ 较小的即可,较后的一定为 $ 0 $。

最终复杂度卡在排序和两次 exgcd 上,为 $ O(n \log n) $。

Tips:还有些小细节,观察所有式子发现,对于所有 $ S $ 和 $ t_i $ 都可以并需要进行 $ S \leftarrow S \bmod{T}, t_i \leftarrow t_i \bmod{T} $,这是对答案无影响的。对于在 set 中排除同环模意义下同位置的,可以考虑利用 C++ 特性,重载小于号,使得只保留相同第一关键字中最先加入的,可以证明最先加入的一定最小。

附:好久没写 exgcd 了,简单重推一下吧:

要求 $ ax + by = \gcd(a, b) $。

显然存在 $ bx' + (a \bmod{b})y' = \gcd(b, a \bmod{b}) $。

又 $ \gcd(a, b) = \gcd(b, a \bmod{b}) $。

整理并展开 $ a \bmod b $ 得到 $ ax + by = bx' + (a - \lfloor \dfrac{a}{b} \rfloor \times b)y' $。

则显然有 $ x = y', y = x' - \lfloor \dfrac{a}{b} \rfloor \times y $。

问题规模缩减,不断递归,直到最终 $ \gcd = 0 $ 则回溯 $ x = 1, y = 0 $ 即可。

此时得到任意解,令 $ d = \dfrac{t_j - t_i}{\gcd(S, T)} $,显然整除,则 $ x \leftarrow x \times d, y \leftarrow y \times d $ 即可,同时注意我们想要求得 $ x $ 得最小非负解,发现方程通解为 $ x + k\dfrac{T}{\gcd(S, T)}, y - k\dfrac{S}{\gcd(S, T)} $,以 $ \dfrac{T}{\gcd(S, T)} $ 为模数对 $ x $ 取模转正后求出对应的 $ y $ 即可找到 $ x $ 的最小非负解。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

int N;
ll S, T;
int A[210000];
int ans[210000];
int gcdv(-1);
struct Node{
    ll v; ll origin; int idx;
    friend const bool operator < (const Node &a, const Node &b){
        return a.v < b.v;
    }
};
unordered_map < int, set < Node > > loops;

void exgcd(ll a, ll b, ll &x, ll &y){
    if(!b)return x = 1, y = 0, void();
    exgcd(b, a % b, x, y);
    int tmp(x);
    x = y, y = tmp - a / b * y;
}
ll CalMnX(ll ti, ll tj){
    ll x, y; ll d = (tj - ti) / gcdv;
    exgcd(S, T, x, y);
    x *= d, y *= d;
    ll P = T / gcdv;
    x = (x % P + P) % P;
    y = (tj - ti - S * x) / T;
    return x;
}

int main(){
    T = read(), N = read();
    for(int i = 1; i <= N; ++i)(S += (A[i] = read())) %= T;
    gcdv = __gcd(S, T);
    loops[0].insert({Node{0, 0, 1}});
    ll cur(0);
    for(int i = 2; i <= N; ++i)
        (cur += A[i]) %= T,
        loops[cur % gcdv].insert(Node{CalMnX(cur % gcdv, cur), cur, i});
    for(auto loop : loops){
        if((int)loop.second.size() == 1){ans[loop.second.begin()->idx] = T / gcdv; continue;}
        for(auto it = loop.second.begin(); it != loop.second.end(); advance(it, 1)){
            auto nxit = it == prev(loop.second.end()) ? loop.second.begin() : next(it);
            ans[it->idx] = CalMnX(it->origin, nxit->origin);
        }
    }
    for(int i = 1; i <= N; ++i)printf("%d%c", ans[i], i == N ? '\n' : ' ');
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

T3

首先记录一下 @sssmzy 的部分分做法:

对于传送门无重叠,加个倍增即可保证复杂度。

对于询问无重叠,考虑暴力建图即可均摊正确复杂度。

正解分块或吉司机线段树。

//TODO (先去学吉司机线段树了。。

UPD

update-2023_02_13 初稿

posted @ 2023-02-15 19:10  Tsawke  阅读(30)  评论(0编辑  收藏  举报