2020牛客暑期多校训练营(第四场)


A. Ancient Distance
https://ac.nowcoder.com/acm/contest/5669/A


B. Basic Gcd Problem

https://ac.nowcoder.com/acm/contest/5669/B

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, c, m;

int primes[N], cnt;     // 存储所有质数
bool st[N];     // 存储每个数是否已被筛掉
void get_primes(int n){      // 线性筛法求素数
    st[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

ll qpow(ll a, ll b) {
    a %= mod;
    ll res = 1;
    while (b) {
        if (b & 1)res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}


int main() {
    ios::sync_with_stdio(false);

    get_primes(1000000);
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &c);
        // 如果是素数 直接输出1
        if (!st[n]) {
            m = 1;
        } else {
            // 如果不是 则统计质因子的个数
            m = 0;
            for (int i = 0; i < cnt && primes[i] <= n; i++) {
                if (n % primes[i] == 0) {
                    while (n % primes[i] == 0) {
                        m++;
                        n /= primes[i];
                    }
                }
                if (!st[n])break;
            }
            if (n > 1) {
                m++;
            }
        }
        printf("%lld\n", qpow(c, m));
    }
    return 0;
}

C. Count New String · 后缀自动机

https://ac.nowcoder.com/acm/contest/5669/C
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
int pos[N];

namespace Sam { // 后缀自动机

    const int M = 2e6 + 10;
    const int CHAR_NUM = 10;
    
    // 后缀自动机
    struct SAM {
        struct state {
            int len, link;
            vector<int> nxt;

            state() { nxt.resize(CHAR_NUM); }
        } st[M * 2];

        int sz;//自动机的大小
        int last;// 未加入下一个字符前最长的前缀(整个串)所属的节点的编号

        void init() {
            // 创建只有一个初始状态的sam
            st[0].len = 0;
            st[0].link = -1;
            sz = 0;
            last = 0;
        }

        void insert(int c) {
            int cur = ++sz;
            st[cur].len = st[last].len + 1;
            int p = last;
            while (p != -1 && !st[p].nxt[c]) {
                st[p].nxt[c] = cur;
                p = st[p].link;
            }

            if (p == -1) {
                st[cur].link = 0;
            } else {
                int q = st[p].nxt[c];
                if (st[p].len + 1 == st[q].len) {
                    st[cur].link = q;

                } else {
                    int clone = ++sz;//复制
                    st[clone].len = st[p].len + 1;
                    st[clone].nxt = st[q].nxt;
                    st[clone].link = st[q].link;
                    while (p != -1 && st[p].nxt[c] == q) {
                        st[p].nxt[c] = clone;
                        p = st[p].link;
                    }

                    st[q].link = st[cur].link = clone;
                }
            }
            last = cur;
        }
    } sam;
}
using namespace Sam;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    string s;
    cin >> s;
    s = " " + s;
    sam.init();
    int n = s.length() - 1;
    pos[n + 1] = 0;
    stack<int> st;// 单调栈 字符从大到小排列
    for (int i = n; i >= 1; i--) {//从后往前遍历
        while (!st.empty() && s[st.top()] < s[i]) st.pop();
        int k = st.empty() ? n + 1 : st.top();//记下 最大字母的下标 根据坐标寻找插入时对应的后缀自动机的状态
        sam.last = pos[k];
        for (int j = i; j < k; j++) {
            sam.insert(s[i] - 'a');
        }

        pos[i] = sam.last;
        st.push(i);
    }

    ll ans = 0;
    // 根据后缀自动机的性质 以点i为节点的子串的个数 = len[i]-len[link[i]]
    for (int i = 1; i <= sam.sz; i++) {
        ans += sam.st[i].len - sam.st[sam.st[i].link].len;
    }
    cout << ans << endl;

    return 0;
}

D Dividing Strings
E Eliminate++


F. Finding the Order

https://ac.nowcoder.com/acm/contest/5669/F

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
char s[310][310];

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int ac, ad, bc, bd;
        scanf("%d%d%d%d", &ac, &ad, &bc, &bd);
        if (ac > ad)puts("AB//DC");
        else if (ac == ad) {
            if (bc > bd)puts("AB//CD");
            else puts("AB//DC");
        } else puts("AB//CD");
    }
    return 0;
}

G Geometry Challenge


H. Harder Gcd Problem

https://ac.nowcoder.com/acm/contest/5669/H

优先处理质因子较大的数,因为质因子越大,含有该质因子的数越少
当包含同一个质因子的数的个数有偶数个时,显然可以将这些数俩俩匹配
如果是奇数的话,就是一群配对里出了个单身狗的故事了,
选一个包含了更小的质因子的数,将其与其他同样包含了更小的质因子的数进行匹配

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef pair<int, int> pii;
int n;

vector<int> v[N];// v[i]存放 包含第i个质因子的数

int primes[N], cnt = 0;     // 存储所有质数
int st[N], vis[N];     // 存储每个数是否已被筛掉

int Mx[N], Mi[N]; // 质因子最大值 质因子最小值
int id[N];

void get_primes(int n){      // 线性筛法求素数
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt] = i;
            id[i] = cnt;
            cnt++;
        }
        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = 1;
            if (i % primes[j] == 0) break;
        }
    }
	// 求出所有质因子后 再筛选 将每个数放到自己最大质因子的位置
	// 用埃筛直接找也是可以的
    for (int i = cnt - 1; i >= 0; i--) {
        for (int j = primes[i]; j <= n; j += primes[i]) {
            if (!vis[j]) {
                vis[j] = 1; 
                Mx[j] = Mi[j] = primes[i];

            } else {
                Mi[j] = min(Mi[j], primes[i]);
            }
        }
    }
}

vector<pii> ans;
int sz;

int main() {
    ios::sync_with_stdio(false);

    get_primes(200000);
    
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 0; i < cnt; i++) {
            v[i].clear();
        }
        ans.clear();

        int lim = 0;
        for (int i = 2; i <= n; i++) {
            v[id[Mx[i]]].push_back(i);
            lim = max(lim, Mx[i]);
        }

        lim = id[lim];

        for (int i = lim; i >= 0; i--) {
            sz = v[i].size();
            if (sz == 1) {
                // 抛弃
            } else if (sz & 1) {//奇数
                sort(v[i].begin(), v[i].end(), [](int a, int b) { // c++14写法
                    return Mi[a] < Mi[b];
                });
                int x = v[i][0];
                if (Mi[x] != primes[i]) {//下放
                    v[id[Mi[x]]].push_back(x);
                } else {
                    //抛弃
                }
                for (int j = 1; j < sz; j += 2) { // 相邻两个进行匹配
                    ans.push_back({v[i][j], v[i][j + 1]});
                }
            } else {//偶数
                for (int j = 0; j < sz; j += 2) {
                    ans.push_back({v[i][j], v[i][j + 1]});
                }
            }
        }

        sz = ans.size();
        printf("%d\n", sz);
        for (int i = 0; i < sz; i++) {
            printf("%d %d\n", ans[i].first, ans[i].second);
        }
    }
    return 0;
}

I Investigating Legions
J Jumping on the Graph

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值