zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    多线程的设计模式
    Deque 双端队列
    并发Queue
    并发类容器
    同步类容器
    java0926
    第十二次作业
    第十一次作业
    第十次
    第九次
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13374974.html
Copyright © 2011-2022 走看看