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

Contest Info


传送门

Solved A B C D E F G H I J
5 / 13 Ø O Ø - - O - O - -
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Ancient Distance

考虑固定了(k),那么可以通过二分来进行check是否合法,具体做法为每次找一个深度最大的点,之后找到他的(mid)级祖先并设为关键点,之后将子树删取再重复此操作。时间复杂度为(O(nlognlog(ans)))
但题目要求枚举(k),发现并不好计算。注意(k)(ans)之间有关系,具体为如果确定了(ans),那么不超过(frac{n}{k+1})个关键点。所以考虑换一下计算答案的方式,改为枚举答案,那么关键点数量为调和级数即(O(nlogn))级别,之后的做法和二分答案类似,总时间复杂度即为(O(nlog^2n))

Code
// Author : heyuhhh
// Created Time : 2020/07/23 10:09:48
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]
";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 2e5 + 5, M = 20;

int n;
vector<int> G[N];
int f[N][M], deep[N];
int in[N], out[N], mp[N], T;
int ans[N];

void dfs(int u, int fa) {
    deep[u] = deep[fa] + 1;
    mp[in[u] = ++T] = u;
    f[u][0] = fa;
    for (int i = 1; i < M; i++) {
        f[u][i] = f[f[u][i - 1]][i - 1];
    }
    for (auto v : G[u]) {
        if (v != fa) {
            dfs(v, u);
        }
    }
    out[u] = T;
}

int find_kth(int u, int k) {
    for (int i = M - 1; i >= 0; i--) {
        if (k >> i & 1) {
            u = f[u][i];
        }
    }
    if (u == 0) u = 1;
    return u;
}

int maxv[N << 2], node[N << 2], cover[N << 2];

void push_up(int o) {
    maxv[o] = node[o] = -1;
    if (!cover[o << 1]) {
        maxv[o] = maxv[o << 1];
        node[o] = node[o << 1];
    }
    if (!cover[o << 1|1] && maxv[o << 1|1] > maxv[o]) {
        maxv[o] = maxv[o << 1|1];
        node[o] = node[o << 1|1];
    }
    cover[o] = (cover[o << 1] & cover[o << 1|1]);
}

void build(int o, int l, int r) {
    if (l == r) {
        maxv[o] = deep[mp[l]];
        node[o] = mp[l];
        cover[o] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1|1, mid + 1, r);
    push_up(o);
}

void update(int o, int l, int r, int L, int R, int v) {
    if (L <= l && r <= R) {
        cover[o] = v;
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid) update(o << 1, l, mid, L, R, v);
    if (R > mid) update(o << 1|1, mid + 1, r, L, R, v);
    push_up(o);
}

void run() {
    for (int i = 1; i <= n; i++) {
        G[i].clear();
    }
    T = 0;
    for (int i = 1; i < n; i++) {
        int x; cin >> x;
        G[x].push_back(i + 1);
        G[i + 1].push_back(x);
    }
    for (int i = 1; i <= n; i++) {
        ans[i] = n - 1;
    }
    dfs(1, 0);
    build(1, 1, T);
    for (int i = n - 1; i >= 0; i--) {
        int cost = 0;
        vector<int> op;
        while (1) {
            ++cost;
            if (maxv[1] - 1 <= i) break;
            int u = node[1];
            int k = find_kth(u, i);
            op.push_back(k);
            update(1, 1, T, in[k], out[k], 1);
        }
        ans[cost] = i;
        for (auto it : op) {
            update(1, 1, T, in[it], out[it], 0);
        }
    }
    for (int i = 2; i <= n; i++) {
        ans[i] = min(ans[i], ans[i - 1]);
    }
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += ans[i];
    }
    cout << sum << '
';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while (cin >> n) run();
    return 0;
}

B. Basic Gcd Problem

签到。

Code
// Author : heyuhhh
// Created Time : 2020/07/20 12:10:55
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5, MOD = 1e9 + 7;
int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int v[N], prime[N];
int num;
void Euler() {
    for(int i = 2; i < N; i++) {
        if(v[i] == 0) {
            v[i] = i;
            prime[++num] = i;
        }
        for(int j = 1; j <= num && prime[j] * i < N; j++) {
            v[prime[j] * i] = prime[j] ;
        }
    }
} 

void run() {
    int n, c;
    cin >> n >> c;
    int x = 0;
    for (int i = 1; 1ll * prime[i] * prime[i] <= n && i <= num; i++) {
        while (n % prime[i] == 0) {
            n /= prime[i];
            ++x;
        }
    }
    if (n > 1) {
        ++x;
    }
    int ans = qpow(c, x);
    cout << ans << '
';
}

int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    Euler();
    int T; cin >> T; while(T--)
    run();
    return 0;
}

C. Count New String

题意:
考虑操作(f(s))(s)串变为其前缀max串,那么对于(s)所有的子串都经过(f)产生一个集合,现在要统计集合中有多少本质不同的子串。
字符集大小不超过(10)

思路:
将题意转化了一下就是上面这个样子。
如果直接求(s)的本质不同子串就是一个模板题,但问题为要将所有子串变为其前缀max串后再求,就棘手许多。
首先容易发现,问题等价于对于所有的后缀进行(f)操作,之后再求本质不同的子串个数。
因为字符集大小不超过(10),所以每个位置变化不会超过(10)次(变化一次就受到前面的影响变得更大),所以考虑直接对所有后缀建一个trie树,树的大小为(O(10cdot n))级别的。
问题就转化为了在trie树上求本质不同的子串个数,也就是多个串的本质不同子串个数,那么直接上广义后缀自动机就好啦。
代码中是先建出了trie树再添加每个字符的,但实际中一般直接在线构造广义后缀自动机比较方便,因为这样不用显式地建出trie树。

Code
// Author : heyuhhh
// Created Time : 2020/07/21 10:14:10
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
// head
const int N = 1e5 + 5, M = 10;

struct SAM {
    const static int MAXNODE = N * M * 2;
    const static int M = 10;  //
    int go[MAXNODE][M], link[MAXNODE], len[MAXNODE];
    int last, sz, root;

    int newnode() {
        ++sz;
        memset(go[sz], 0, sizeof(go[sz]));
        return sz;
    }

    void init() {
        sz = 0;
        root = last = newnode();
        len[root] = link[root] = 0;
    }

    int split(int p, int q, int ch) {
        int clone = newnode();
        memcpy(go[clone], go[q], sizeof(go[q]));
        link[clone] = link[q];
        link[q] = clone;
        len[clone] = len[p] + 1;
        for (int i = p; i && go[i][ch] == q; i = link[i]) {
            go[i][ch] = clone;
        }
        return clone;
    }

    void insert(int ch) {
        if (go[last][ch]) {
            int q = go[last][ch];
            last = len[last] + 1 == len[q] ? q : split(last, q, ch);
            return;
        }
        // ----
        int cur = newnode();
        len[cur] = len[last] + 1;
        int p = last;
        for (; p && !go[p][ch]; p = link[p]) {
            go[p][ch] = cur;
        }
        if (!p) {
            link[cur] = root;
        } else {
            int q = go[p][ch];
            link[cur] = len[p] + 1 == len[q] ? q : split(p, q, ch);
        }
        last = cur;
    }

    ll solve() {
        ll ans = 0;
        for (int i = root + 1; i <= sz; ++i) {
           ans += len[i] - len[link[i]];
        }
        return ans;
    }
}sam;
int sam_id[N * M];
struct Trie {
    int ch[N * M][M];
    int root, cnt, last;

    int newnode() {
        memset(ch[++cnt], 0, sizeof(ch[cnt]));
        return cnt;
    }

    void init() {
        cnt = 0;
        root = newnode();
    }

    int insert(int last, int x) {
        if (!ch[last][x]) {
            ch[last][x] = newnode();
        }
        return ch[last][x];
    }

    void bfs() {
        int p = root;
        sam.init();
        sam_id[root] = sam.root;
        queue <int> q;
        q.push(p);
        while (!q.empty()) {
            int u = q.front(); 
            q.pop();
            for (int i = 0; i < M; i++) {
                if (ch[u][i]) {
                    sam.last = sam_id[u];
                    sam.insert(i);
                    sam_id[ch[u][i]] = sam.last;
                    q.push(ch[u][i]);
                }
            }
        }
    }
}trie;

string s;
int n;
bool valid[N][M];
int trie_id[N][M];

void run() {
    cin >> s;
    int n = s.length();
    for (int i = 0; i < n; i++) {
        valid[i][s[i] - 'a'] = true;
        if (i > 0) {
            for (int j = 0; j < 10; j++) {
                if (valid[i - 1][j]) {
                    valid[i][max(j, s[i] - 'a')] = true;
                }
            }
        }
    }
    trie.init();
    for (int i = 0; i < 10; i++) {
        if (valid[n - 1][i]) {
            trie_id[n - 1][i] = trie.insert(trie.root, i);
        }
    }
    for (int i = n - 1; i > 0; i--) {
        for (int j = 0; j < 10; j++) {
            if (valid[i - 1][j]) {
                for (int k = 0; k < 10; k++) {
                    if (valid[i][k] && max(j, s[i] - 'a') == k) {
                        trie_id[i - 1][j] = trie.insert(trie_id[i][k], j);
                    }
                }
            }
        }
    }
    trie.bfs();
    ll ans = sam.solve();
    cout << ans << '
';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

F. Finding the Order

找最长的判断一下就行。

Code
// Author : heyuhhh
// Created Time : 2020/07/20 12:28:20
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    int a, b, c, d;
    vector<int> v(4);
    cin >> a >> b >> c >> d;
    v[0] = a, v[1] = b, v[2] = c, v[3] = d;
    sort(all(v));
    if (v[3] != v[2]) {
        if (b == v[3] || c == v[3]) {
            cout << "AB//CD" << '
';
        } else {
            cout << "AB//DC" << '
';
        }
    } else {
        if (a == d && a == v[3]) {
            cout << "AB//DC" << '
';
        } else {
            cout << "AB//CD" << '
';
        }
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

H. Harder Gcd Problem

从大到小来筛素数的倍数即可,如果一共有偶数个那么两两匹配,否则留下(2p)到最后匹配。
显然这样是最优的。

Code
#include<bits/stdc++.h>
 
using namespace std;
 
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LC k<<1
#define RC k<<1|1
 
typedef long long LL;
const int N=210000;
const int M=1100000;
const LL mod=1e9+7;
 
int T,n;
int prim[1100000],primm;
bool valid[11000000];
void getprime(int n)
{
    memset(valid,1,sizeof(valid));
    valid[1]=false;
    for(int i=2;i<=n;i++)
    {
        if(valid[i]) prim[++primm]=i;
        for(int j=1;j<=n&&i*prim[j]<=n;j++)
        {
            valid[i*prim[j]]=false;
            if(i%prim[j]==0)break;
        }
    }
}
int num;
int pp[N];
vector<int> f[N];
vector<pair<int,int> > ans;
int main()
{
    getprime(400000);
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            pp[i]=0,f[i].clear();
        int l=1;
        while (prim[l+1]<=n) l++;
        for (int i=l;i>=1;i--)
        {
            int p=prim[i];
            while (p<=n)
            {
                if (!pp[p]) f[i].pb(p),pp[p]=1;
                p+=prim[i];
            }
        }
        ans.clear();
        for (int i=l;i>=1;i--)
        {
            if (f[i].size()==1) continue;
            if (f[i].size()%2==0)
            {
                for (int j=0;j<f[i].size();j+=2)
                    ans.pb(mp(f[i][j],f[i][j+1]));
            }
            else
            {
                ans.pb(mp(f[i][0],f[i][2]));
                for (int j=3;j<f[i].size();j+=2)
                    ans.pb(mp(f[i][j],f[i][j+1]));
                if (i!=1) f[1].pb(f[i][1]);
            }
        }
        printf("%d
",ans.size());
        for (int i=0;i<ans.size();i++)
            printf("%d %d
",ans[i].fi,ans[i].se);
    }
    return 0;
}