• 2022 年杭电多校第九场补题记录


    A Arithmetic Subsequence

    题意:给定一个长度为 n n n 的序列,对它进行重新排序使得它不存在等差子序列 n ≤ 5 × 1 0 3 n \leq 5\times 10^3 n5×103

    解法:首先如果一个数字出现大于等于 3 3 3 次,则一定无解。

    考虑一个等差子序列的二进制构成: x , y , z x,y,z x,y,z。首先都丢弃掉共同的最低位的 0 0 0,考虑公差为 1 1 1 的情况(公差更大的同理),则有 x = ( 01 ⋯ 01 ) x=(01\cdots 01) x=(0101) y = ( 01 ⋯ 10 ) y=(01\cdots 10) y=(0110) z = x = ( 01 ⋯ 11 ) z=x=(01\cdots 11) z=x=(0111),前面完全相同。则只有让 y y y 排列在 x , z x,z x,z 之前或者之后才行。注意到 x , z x,z x,z 同奇偶而 y y y 不同,因而可以按照末尾对其排序,则可以顺利让 x , z x,z x,z 置于 y y y 前或后。同理对公差更大的序列也可以进行类似的操作。因而最后的答案就是对数字按照 b i t r e v \rm bitrev bitrev 的大小进行排序即可。总时间复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)

    #include 
    using namespace std;
    unsigned rev(unsigned x)
    {
        unsigned ans = 0;
        for (int i = 31; i >= 0; i--, x >>= 1)
            ans |= (x & 1) << i;
        return ans;
    }
    int main()
    {
        int t, n;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d", &n);
            map<int, int> times;
            vector<pair<unsigned, unsigned>> a(n);
            bool flag = 1;
            for (int i = 0; i < n;i++)
            {
                scanf("%u", &a[i].second);
                times[a[i].second]++;
                a[i].first = rev(a[i].second);
            }
            for(auto i:times)
                if(i.second >= 3)
                {
                    flag = 0;
                    break;
                }
            if(!flag)
            {
                printf("NO\n");
                continue;
            }
            printf("YES\n");
            sort(a.begin(), a.end());
            for (auto i : a)
                printf("%d ", i.second);
            printf("\n");
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    C Fast Bubble Sort

    题意:给定长度为 n n n 的排列 { p i } \{p_i\} {pi} q q q 次询问 [ l , r ] [l,r] [l,r],问该区间需要经过多少次子区间旋转平移操作(即, a i a i + 1 ⋯ a j → a i + 1 ⋯ a j a i a_ia_{i+1} \cdots a_j \to a_{i+1} \cdots a_ja_i aiai+1ajai+1ajai a i a i + 1 ⋯ a j → a j a i a i + 1 ⋯ a j − 1 a_ia_{i+1} \cdots a_j \to a_ja_ia_{i+1} \cdots a_{j-1} aiai+1ajajaiai+1aj1),变成 B ( p [ l , r ] ) B(p[l,r]) B(p[l,r])。其中 B ( p [ x , y ] ) B(p[x,y]) B(p[x,y]) 操作如下:

    int* B(int *p, int x, int y)
    {
        for(int i=x;i<y;i++)
            if(p[i]>p[i+1])
                swap(p[i],p[i+1]);
        return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    n , q ≤ 1 × 1 0 5 n,q \leq 1\times 10^5 n,q1×105

    解法:利用单调栈求出每个点后面第一个比它大的值在哪里,第 i i i 个数字记为 R i R_i Ri。若 r i = i + 1 r_i=i+1 ri=i+1,则必然不会交换;否则它就会有交换操作,且直接交换到 R p R_p Rp。对应于可执行操作,就是一次旋转平移。同时,若满足 i < j ≤ r j ≤ r i i < j \leq r_j \leq r_i i<jrjri,则 j j j 不会被交换。考虑倍增, f i , j f_{i,j} fi,j 表示从第 i i i 个点开始经历 2 j 2^j 2j 次旋转平移操作所到达的点。若抵达最后一个跳跃点 p p p,且 R p ≥ r R_p \geq r Rpr,若 R p = p + 1 R_p=p+1 Rp=p+1 则不经历旋转平移,否则还需要一次旋转平移。

    #include
    using namespace std;
    const int N = 100000;
    int a[N + 5], r[N + 5], nx[N + 5], f[N + 5][20];
    int main()
    {
        int t, n, q;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d%d", &n, &q);
            for (int i = 1; i <= n;i++)
            {
                r[i] = n + 1;
                for (int j = 0; j < 20;j++)
                    f[i][j] = 0;
            }
            for (int j = 0; j < 20;j++)
                f[n + 1][j] = n + 1;
            for (int i = 1; i <= n; i++)
                scanf("%d", &a[i]);
            stack<int> s;
            for (int i = n; i >= 1;i--)
            {
                while (!s.empty() && a[s.top()] < a[i])
                    s.pop();
                if(!s.empty())
                    r[i] = s.top();
                else
                    r[i] = n + 1;
                s.push(i);
            }
            f[n + 1][0] = nx[n + 1] = n + 1;
            for (int i = n; i >= 1;i--)
            {
                if(r[i] > i + 1)
                {
                    nx[i] = i + 1;
                    f[i][0] = r[i];
                }
                else
                {
                    f[i][0] = f[r[i]][0];
                    nx[i] = nx[i + 1];
                }
            }
            for (int i = 1; i < 20; i++)
                for (int j = 1; j <= n + 1; j++)
                    f[j][i] = f[f[j][i - 1]][i - 1];
            while(q--)
            {
                int l, r;
                scanf("%d%d", &l, &r);
                int ans = 0, place = l;
                for (int i = 19; i >= 0;i--)
                    if (f[place][i] <= r)
                    {
                        place = f[place][i];
                        ans += 1 << i;
                    }
                printf("%d\n", ans + (nx[place] <= r));
            }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    F Mario Party

    题意:给定长度为 n n n 的序列 { a n } \{a_n\} {an} q q q 次询问,每次从 l l l 号节点出发,初始钱数为 x x x,走到 i + 1 i+1 i+1 节点的时候若 x + a i + 1 ≥ 0 x+a_{i+1} \geq 0 x+ai+10 x ← x + a i + 1 x \leftarrow x+a_{i+1} xx+ai+1,否则不变,问抵达 r r r 节点时的钱数。 n , q ≤ 5 × 1 0 5 n,q \leq 5\times 10^5 n,q5×105 ∑ ∣ a i ∣ ≤ 1 × 1 0 6 \sum|a_i| \leq 1\times 10^6 ai1×106

    解法:考虑在第 i i i 个节点的时候,两次询问里面人的钱数相同,则他们两个可以认为是一个人。

    首先离线,然后从左到右遍历序列的时候维护一个询问的并查集。对于 a i ≥ 0 a_i \geq 0 ai0 的情况,那么显然它不会合并询问;否则,对于钱数为 x x x 和钱数为 x − a i x-a_i xai 的询问,就需要合并成一个询问。

    考虑加入询问:如果当前的钱数没人了,则新建一个点;否则合并入并查集中,同时利用并查集维护每个询问的起始钱数对应于现在的钱数。回答只需要在并查集中查询对应的钱数即可。总时间复杂度 O ( n + q ) \mathcal O(n+q) O(n+q)

    #include 
    using namespace std;
    const int N = 1000000;
    int p[3 * N + 5], pos[3 * N + 5];
    class DSU
    {
        vector<int> father;
    
    public:
        DSU(int n)
        {
            father.resize(n);
            for (int i = 0; i < n;i++)
                father[i] = i;
        }
        int getfather(int x)
        {
            return father[x] == x ? x : father[x] = getfather(father[x]);
        }
        void merge(int v, int u)
        {
            u = getfather(u), v = getfather(v);
            if (u != v)
                father[u] = v;
        }
    };
    int main()
    {
        int caset, n, q;
        scanf("%d", &caset);
        for (int o = 1; o <= caset;o++)
        {
            scanf("%d%d", &n, &q);
            for (int i = 0; i <= 3 * N; i++)
            {
                p[i] = -1;
                pos[i] = 0;
            }
            DSU t(q);
            auto merge = [&](int u, int v)
            {
                assert(u <= 2 * N && u >= 0);
                assert(v <= 2 * N && v >= 0);
                if (p[u] == -1)
                    return;
                if (p[v] == -1)
                {
                    p[v] = p[u];
                    pos[p[v]] = v;
                }
                else
                    t.merge(p[v], p[u]);
                p[u] = -1;
            };
            int start = N;
            vector<int> a(n), ans(q), x(q);
            vector<vector<int>> add(n), del(n);
            for (int i = 0; i < n;i++)
                scanf("%d", &a[i]);
            for (int i = 0, l, r; i < q;i++)
            {
                scanf("%d%d%d", &l, &r, &x[i]);
                add[l - 1].push_back(i);
                del[r - 1].push_back(i);
            }
            for (int i = 0; i < n;i++)
            {
    
                if (a[i] < 0)
                    for (int j = start; j < start - a[i];j++)
                        merge(j, j - a[i]);
                start -= a[i];
                for (auto j : add[i])
                {
                    int now = x[j] + start;
                    if(p[now] == -1)
                    {
                        p[now] = j;
                        pos[j] = now;
                    }
                    else
                        t.merge(p[now], j);
                }
                for (auto j : del[i])
                    ans[j] = pos[t.getfather(j)] - start;
            }
            for (auto i : ans)
                printf("%d\n", i);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    G Matryoshka Doll

    题意:给定 n n n 个盒子的大小 { a i } \{a_i\} {ai},一个大小为 x x x 的盒子能放入大小为 y y y 的盒子中当且仅当 x + r ≤ y x+r \leq y x+ry。问将这 n n n 个盒子分成 k k k 组,每一组中小盒子都能完全放入本组任何比它大的盒子中的方案数。 n , k ≤ 5 × 1 0 3 n,k \leq 5\times 10^3 n,k5×103

    解法:首先按照大小对 a i a_i ai 排序。考虑 f i , j f_{i,j} fi,j 表示前 i i i 小的盒子分成 j j j 堆的方案数,则 f i , j ← f i − 1 , j − 1 + f i − 1 , j max ⁡ ( 0 , j − c i ) f_{i,j} \leftarrow f_{i-1,j-1}+f_{i-1,j}\max(0,j-c_i) fi,jfi1,j1+fi1,jmax(0,jci),其中 c i c_i ci 表示前 i i i 个盒子中有多少个盒子不能放入第 i i i 个盒子。 f i − 1 , j max ⁡ ( 0 , j − c i ) f_{i-1,j}\max(0,j-c_i) fi1,jmax(0,jci) 的含义为,第 i i i 个盒子只能和前面所有的堆中,最大的盒子满足 a j + r ≤ a i a_j+r \leq a_i aj+rai 放在一堆,对于不能被 a i a_i ai 放进去的盒子,它一定会被单独的列在别的堆中。预处理 c i c_i ci 后总时间复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

    #include 
    using namespace std;
    const int mod = 998244353;
    const int N = 5000;
    int f[N + 5][N + 5], a[N + 5], l[N + 5];
    int main()
    {
        int t, n, k, r;
        scanf("%d", &t);
        while(t--)
        {
            memset(f, 0, sizeof(f));
            scanf("%d%d%d", &n, &k, &r);
            for (int i = 1; i <= n;i++)
                scanf("%d", &a[i]);
            sort(a + 1, a + n + 1);
            l[1] = 1;
            for (int i = 2; i <= n;i++)
            {
                int st = l[i - 1];
                while(st <= i && a[st] <= a[i] - r)
                    st++;
                l[i] = st;
            }
            for (int i = 1; i <= n;i++)
                f[i][i] = 1;
            for (int i = 2; i <= n;i++)
                for (int j = 1; j < i && j <= k;j++)
                    f[i][j] = (f[i - 1][j - 1] + 1ll * f[i - 1][j] * max(0, j - (i - l[i])) % mod) % mod;
            printf("%d\n", f[n][k]);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    H Shortest Path in GCD Graph

    题意:有 n n n 个点的图,第 i i i 个节点和第 j j j 个节点之前有一条边权为 gcd ⁡ ( i , j ) \gcd(i,j) gcd(i,j) 的边。 q q q 次询问从 x x x y y y 节点的最短路长度和最短路条数。 n ≤ 1 × 1 0 7 n\leq 1\times 10^7 n1×107 q ≤ 5 × 1 0 4 q \leq 5\times 10^4 q5×104

    解法:若 gcd ⁡ ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1,则最短路长度为 1 1 1,条数也为 1 1 1。否则一定可以通过 i → 1 → j i \to 1 \to j i1j 以长度为 2 2 2 抵达。考虑符合条件的最短路条数,必须满足 gcd ⁡ ( x , i ) = 1 \gcd(x,i)=1 gcd(x,i)=1 gcd ⁡ ( y , i ) = 1 \gcd(y,i)=1 gcd(y,i)=1,即是计算 ∑ i = 1 n [ gcd ⁡ ( i , l c m ( x , y ) ) = 1 ] \displaystyle \sum_{i=1}^n [\gcd(i,{\rm lcm}(x,y))=1] i=1n[gcd(i,lcm(x,y))=1]。设 u = l c m ( x , y ) u={\rm lcm}(x,y) u=lcm(x,y),有:
    ∑ i = 1 n [ gcd ⁡ ( i , u ) = 1 ] = ∑ i = 1 n ∑ d ∣ u μ ( d ) = ∑ d ∣ u μ ( d ) ∑ i = 1 ⌊ n d ⌋ 1 = ∑ d ∣ u μ ( d ) ⌊ n d ⌋ ni=1[gcd(i,u)=1]=ni=1d|uμ(d)=d|uμ(d)ndi=11=d|uμ(d)nd i=1n[gcd(i,u)=1]===i=1nduμ(d)duμ(d)i=1dn1duμ(d)dn
    因而现在只需要枚举 l c m ( x , y ) {\rm lcm}(x,y) lcm(x,y) n n n 小的因子。对 u , v u,v u,v 进行质因子分解,统计 u u u 的质因子,然后 2 k 2^k 2k 枚举所有质因子组合即可。由于 1 × 1 0 7 1\times 10^7 1×107 下至多只有 8 8 8 个质因子, l c m ( x , y ) {\rm lcm}(x,y) lcm(x,y)最多能包含的本质不同的质因子只有 12 12 12 个,因而完全可以通过。总复杂度 O ( q 2 k ) \mathcal O(q2^k) O(q2k),其中 k ≤ 12 k \leq 12 k12

    #include 
    using namespace std;
    const int N = 1e7 + 5;
    bool vis[N + 5];
    int tot, prime[N + 5], mu[N + 5], minfactor[N + 5];
    void sieve(int n)
    {
        mu[1] = 1;
        for (int i = 2; i <= n;i++)
        {
            if(!vis[i])
            {
                minfactor[i] = i;
                prime[++tot] = i;
                mu[i] = -1;
            }
            for (int j = 1; j <= tot && (long long)prime[j] * i <= n;j++)
            {
                int num = prime[j] * i;
                vis[num] = 1;
                mu[num] = -mu[i];
                minfactor[num] = minfactor[i];
                if (i % prime[j] == 0)
                {
                    mu[num] = 0;
                    break;
                }
            }
        }
    }
    int q, n, m, u, v, ans;
    vector<int> factor;
    void fac(int x)
    {
        while (x > 1)
        {
            factor.push_back(minfactor[x]);
            x /= minfactor[x];
        }
    }
    void dfs(int step, long long val)
    {
        if (val > n)
            return;
        if (step == m)
        {
            ans += mu[val] * (n / val);
            return;
        }
        dfs(step + 1, val * factor[step]);
        dfs(step + 1, val);
    }
    int main()
    {
        sieve(N);
        scanf("%d%d", &n, &q);
        while(q--)
        {
            scanf("%d%d", &u, &v);
            int d = __gcd(u, v);
            if (d == 1)
            {
                printf("1 1\n");
                continue;
            }
            printf("2 ");
            factor.clear();
            fac(u), fac(v);
            sort(factor.begin(), factor.end());
            factor.erase(unique(factor.begin(), factor.end()), factor.end());
            m = factor.size();
            ans = 0;
            dfs(0, 1);
            printf("%d\n", ans + (d == 2));
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    J Sum Plus Product

    题意:给定长度为 n n n 的序列 { a i } \{a_i\} {ai},每次等概率随机选择两个数字 a , b a,b a,b,将它们从序列中删除,并添加 a b + a + b ab+a+b ab+a+b,问最终得到数字的期望。 n ≤ 500 n \leq 500 n500

    解法:整个序列的 ∏ i = 1 n ( a i + 1 ) \displaystyle \prod_{i=1}^n (a_i+1) i=1n(ai+1) 在除了最后一次操作的时候是不会变的,最后一次操作会让它减少 1 1 1,因而输出 ∏ i = 1 n ( a i + 1 ) − 1 \displaystyle \prod_{i=1}^n (a_i+1)-1 i=1n(ai+1)1 即可。

    K The Alchemist of the Discrete Mathematics

    题意:给定质数 p p p,问 F p {\Bbb F}_p Fp 中有多少个集合 S S S 满足存在一个域上 n n n 次可约多项式 f ( x ) = g ( x ) k ( x ) f(x)=g(x)k(x) f(x)=g(x)k(x),且 S = T ∩ F p S=T \cap {\Bbb F}_p S=TFp,其中 T T T 为模意义方程 f ( x ) g ( c x ) ≡ 0 ( m o d p ) f(x)g(cx) \equiv 0 \pmod p f(x)g(cx)0(modp) 的解集。 p ≤ 5 × 1 0 5 p\leq 5\times 10^5 p5×105 n n n 无限制, c ∈ F p c \in {\Bbb F}_p cFp

    解法:显然在 c c c 的作用下, F p {\Bbb F}_p Fp 被分成了 p − 1 o r d ( c ) \dfrac{p-1}{{\rm ord}(c)} ord(c)p1 个长度为 o r d ( c ) {\rm ord}(c) ord(c) 的环,和 0 0 0 构成的自环。由于 c c c 的作用,选择了 x x x 就可以顺带选择 c − 1 x c^{-1}x c1x(环上下一个点)。因而对于 h ( x ) h(x) h(x) 上连续 l l l 个零点,只需要 ⌈ l 2 ⌉ \left \lceil \dfrac{l}{2}\right \rceil 2l f ( x ) f(x) f(x) 零点即可——若需要连续的 x 1 , x 2 x_1,x_2 x1,x2,其中 x 1 = c x 2   m o d   p x_1=cx_2 \bmod p x1=cx2modp,则可以让 x − x 1 ∣ g ( x ) x-x_1|g(x) xx1g(x),这样 x 2 x_2 x2 就为 g ( c x ) g(cx) g(cx) 的根。因而该问题可以等价于环上选择若干个不相邻的点的方案数,为 ( n − s s ) + ( n − s − 1 s − 1 ) \displaystyle {n-s \choose s}+{n-s-1 \choose s-1} (sns)+(s1ns1),其中 n n n 为环长, s s s f ( x ) f(x) f(x) 的根(不相邻点数目)。最后还原到 h ( x ) h(x) h(x) 的根的选择,即是这 s s s 个后续节点(因为在选择不相邻点的时候,是将该点与下一个点绑定了)可选可不选。同时需要特判 n = 2 s n=2s n=2s n = 2 s − 1 n=2s-1 n=2s1 这两种情况:对于 n = 2 s n=2s n=2s,朴素选点只有两种选法,但是这两种选法下全选方案其实是一样的,需要减掉一种;对于 n = 2 s − 1 n=2s-1 n=2s1,在环上选点方案为 0 0 0,但是其实有一种,且仅有一种: f ( x ) f(x) f(x) 选择 1 , 3 , 5 ⋯   , n 1,3,5\cdots,n 1,3,5,n,对应于 h ( x ) h(x) h(x) 的全选。因而最后可以得到的 f ( x ) f(x) f(x) 中选择 s s s 个零点,对应于 h ( x ) h(x) h(x) 的零点方案数为 2 s ( ( n − s s ) + ( n − s − 1 s − 1 ) ) − [ n = 2 s ] + [ n + 1 = 2 s ] \displaystyle 2^s\left({n-s \choose s}+{n-s-1 \choose s-1}\right) -[n=2s]+[n+1=2s] 2s((sns)+(s1ns1))[n=2s]+[n+1=2s]

    考虑 k k k 个环,即是使用生成函数做 k k k 次幂即可。最后选择不超过 n n n f ( x ) f(x) f(x) 的根,因为对于域上的多项式,任意次幂均有不可约多项式。此处需要它不能产生 F p {\Bbb F}_p Fp 上的根,那么如果仅选择了 m m m 个零点,只需要找一个 n − m n-m nm 次的不可约多项式即可,此处 n − m ≥ 2 n-m \geq 2 nm2,因为一次不可约多项式可以产生 F p \Bbb F_p Fp 上的根。

    int get_ord(int p, int c)
    {
        int val = 1, ord = 0;
        while (!ord || val != 1)
        {
            ord++;
            val = 1ll * val * c % p;
        }
        return ord;
    }
    long long cal(int n, int s)
    {
        int ans = th[s] * (C(n - s, s) + C(n - s - 1, s - 1)) % P;
        if (n + 1 == 2 * s)
            inc(ans, 1);
        if (n == 2 * s)
            dec(ans, 1);
        return ans;
    }
    int main()
    {
        th[0] = 1;
        for (int i = 1; i < L;i++)
            th[i] = th[i - 1] * 2 % P;
        printf("%d %d\n", cal(4, 2), cal(7, 4));
        int t, n, p, c;
        scanf("%d", &t);
        while (t--)
        {
            scanf("%d%d%d", &p, &c, &n);
            int len = get_ord(p, c), cnt = (p - 1) / len;
            Poly f(p + 1, 0);
            for (int i = 0; i <= (len + 1) / 2;i++)
                f[i] = cal(len, i);
            Poly g = f.power(p + 1, cnt);
            int ans = 0;
            for (int i = 0; i <= min(p, n);i++)
            {
                inc(ans, g[i]);
                if (i)
                    inc(ans, g[i - 1]);
            }
            if (n == 1)
                dec(ans, 1);
            printf("%d\n", ans);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
  • 相关阅读:
    【sklearn】fit()、transform()和fit_transform()的区别
    ViTag :在线 WiFi 精细时间测量辅助多人环境中的视觉-运动身份关联
    【AXI】解读AXI协议乱序机制
    socket 踩坑日记
    如何用看板工具做轻量级项目管理
    自制操作系统日志——第二十一天
    【康耐视国产案例】智能AI相机联合OSARO为Zenni眼镜实现订单履约自动化
    怎么把加密的PDF解密?安利几个办公小技巧
    自动化运维:Ansible基础与命令行模块操作
    【RuoYi-Vue-Plus】学习笔记 42 - Easy Excel(二)Excel 2007(*.xlsx)导入流程分析(源码)
  • 原文地址:https://blog.csdn.net/m0_52048145/article/details/126377458