• 牛课多校第一场


    A - B-Suffix Array (后缀排序)

    题意

    字符串函数(B(t_1t_2...t_k) = b_1b_2...b_k)满足:

    • 如果存在(j<i)(t_j=t_i)(b_i=min_{1 le j < i,t_j=t_i}{i-j})
    • 否则,(b_i=0)

    求字符串(t_1t_2...t_k)的每个后缀的B函数序列的排位(从小到大)。
    字符串只包含a, b两种字符。

    思路

    假设当前字符到相同的下一个字符的距离是k,如果不存在下一个字符,则k=inf。
    举几个例子:

    • abbbba,距离为5,对应的B序列为 001115
    • abbba,距离为4,对应的B序列为 00114
    • aba,距离为2,对应的B序列为 002
    • aa,距离为1,对应的B序列为 01
    • abbb,距离为inf,对应的B序列为 00111

    观察可以发现,这个距离越大,这一段对应的B序列就越小,所以可以计算出每个位置的距离k值,然后按照这个k值后缀排序即可。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <vector>
    #include <cstring>
    #include <string>
    #include <deque>
    #include <cmath>
    #include <iomanip>
    #include <cctype>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res2.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res2.txt","w",stdout)
    #define pb push_back
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N)
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 2e5 + 10;
    const double eps = 1e5;
     
    int last[N];
    string s;
    typedef pair<int, int> PII;
    vector<PII> num;
    int n, w, sa[N], rk[N << 1], oldrk[N << 1];
    int ans[N];
     
    int main() {
        //FILE;
        IOS;
        int n;
        while(cin >> n) {
            num.clear();
            cin >> s;
            int prea = -1, preb = -1;
            for(int i = 0; i < n; i++) last[i] = i;
            for(int i = 0; i < s.size(); i++) {
                if(s[i] == 'a') {
                    if(prea != -1) last[prea] = i;
                    prea = i;
                } else {
                    if(preb != -1) last[preb] = i;
                    preb = i;
                }
            }
            for(int i = 0; i <= min(N, 3 * n + 10); i++) rk[i] = 0;
            int mx = 0;
            for(int i = 0; i < s.size(); i++) {
                num.push_back(mp(last[i] - i, i));
                mx = max(last[i] - i, mx);
            }
            mx++;
            int i, p;
            for (i = 1; i <= n; i++) {
                rk[i] = num[i - 1].first;
                if(rk[i]) rk[i] = mx - rk[i] + 1;
                else rk[i] = rk[i] + 1;
            }
            for (w = 1; w < n; w <<= 1) {
                for (i = 1; i <= n; ++i) sa[i] = i;
                sort(sa + 1, sa + n + 1, [](int x, int y) {return rk[x] == rk[y] ? rk[x + w] < rk[y + w] : rk[x] < rk[y];});
                for(int i = 0; i <= min(N, 2 * n); i++) oldrk[i] = rk[i];
                for(p = 0, i = 1; i <= n; i++) {
                    if(oldrk[sa[i]] == oldrk[sa[i - 1]] && oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) {
                        rk[sa[i]] = p;
                    } else {
                        rk[sa[i]] = ++p;
                    }
                }
            }
            for(int i = 1; i <= n; i++) {
                ans[rk[i]] = num[i - 1].second + 1;
            }
            for(int i = 1; i <= n; i++) cout << ans[i] << " 
    "[i == n];
        }
         
    }
    

    B - Infinite Tree (虚树)

    题意

    Let (mathrm{mindiv}) be the minimum divisor greater than 1 of n. Bobo construsts a tree on all positive integer numbers ({1, 2, dots, }) by adding edges between (n) and (frac{n}{mathrm{mindiv}(n)}) for all (n > 1).

    Let (delta(u, v)) be the number of edges between vertices u and v on the tree. Given (m) and (w_1dots w_m) , Bobo would like to find (min_{u} sum_{i = 1}^{m} w_i delta(u, i!)).

    思路

    这题使用了虚树的技巧。
    虚树就是丢弃多余的结点,只保留待处理的结点和它们的公共祖先。
    构造虚树方法是以dfs序遍历待处理的结点,用一个栈来保存当前处理的结点。每加入一个新结点,求出它与栈顶结点的LCA。有两种情况:

    • LCA和栈顶结点相同,说明新结点和栈顶结点同在一条链中。于是新结点连边入栈。
    • LCA和栈顶结点不同,说明新结点和栈顶结点不在一条链中。一直弹栈到栈顶结点为LCA,然后新结点连边入栈。

    可以看出,这里的栈维护的是一条树链。

    所以此题的数据范围巨大,显然我们只能保留n!的结点。构造出这个n!的虚树后,然后用类似dp的方法就可以求出答案。

    所以任务就是构建虚树。首先,1!到n!的n个结点已经是dfs序了。观察可得,一个数在该树上的到根结点的路径长度和数的质因数相等,且较大的质因数首先影响影响路径。例如:

    • 6的路径:3 -> 2
    • 30的路径:5 -> 3 -> 2

    所以相邻两个点的LCA的深度就是新结点新增的最大质因数之前的质因数个数。比如栈顶是6,插入30,新增最大质因数是5,在路径3 -> 2之前比5大的个数为0,所以LCA深度是0。可以画图证明。
    详见代码

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <vector>
    #include <cstring>
    #include <string>
    #include <deque>
    #include <cmath>
    #include <stack>
    #include <iomanip>
    #include <cctype>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res.txt","w",stdout)
    #define pb push_back
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N)
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 2e5 + 10;
    const double eps = 1e5;
    int w[N];
    int arr[N];
    int mindiv[N];
    int dep[N];
    int lcadep[N];
    int n;
    vector<int> np[N];
    ll val[N];
    ll allv;
    ll tans;
    int si;
    
    int lowbit(int x) {
        return x&(-x);
    }
    void add(int p, int val) {
        while(p <= n) {
            arr[p] += val;
            p += lowbit(p);
        }
    }
    int get(int p) {
        int res = 0;
        while(p) {
            res += arr[p];
            p -= lowbit(p);
        }
        return res;
    }
     
    void prework() {
        for(int i = 2; i < N; i++) {
            if(mindiv[i]) continue;
            mindiv[i] = i;
            for(int j = 2 * i; j < N; j += i) {
                if(!mindiv[j]) mindiv[j] = i;
            }
        }
    }
     
    void adde(int u, int v) {
        np[u].push_back(v);
        np[v].push_back(u);
    }
     
    int st[N];
    int top;
    
    void build() {
        top = 0;
        st[++top] = 1;
        si = n;
        for(int i = 2; i <= n; i++) {
            dep[i] = dep[i - 1] + 1;
            int mxd = i;
            for(; mxd != mindiv[mxd]; mxd /= mindiv[mxd]) dep[i]++;
            lcadep[i] = get(n) - get(mxd - 1);
            for(int j = i; j != 1; j /= mindiv[j]) add(mindiv[j], 1);
        }
        for(int i = 2; i <= n; i++) {
            while(top > 1 && dep[st[top - 1]] >= lcadep[i]) {
                adde(st[top - 1] ,st[top]);
                top--;
            }
            if(dep[st[top]] != lcadep[i]) {
                dep[++si] = lcadep[i];
                adde(si, st[top]);
                st[top] = si;
            }
            st[++top] = i;
        }
        while(top > 1) {
            adde(st[top - 1], st[top]);
            top--;
        }
    }
     
    ll dfs(int p, int fa) {
        ll res = 0;
        for(int nt : np[p]) {
            if(nt == fa) continue;
            res += dfs(nt, p);
        }
        val[p] = res + w[p];
        return val[p];
    }
     
    void solve(int p, int fa, ll ans) {
        tans = min(ans, tans);
        for(int nt : np[p]) {
            if(nt == fa) continue;
            int d = dep[nt] - dep[p];
            ans += d * (allv - 2 * val[nt]);
            solve(nt, p, ans);
            ans -= d * (allv - 2 * val[nt]);
        }
    }
     
    void init() {
        allv = tans = top = 0;
        for(int i = 0; i <= si; i++) {
            np[i].clear();
            val[i] = w[i] = lcadep[i] = dep[i] = arr[i] = 0;
        }
    }
     
    int main() {
        IOS;
        //FILE;
        prework();
        while(cin >> n) {
            init();
            for(int i = 1; i <= n; i++) {
                cin >> w[i];
                allv += w[i];
            }
            build();
            dfs(1, 0);
            for(int i = 1; i <= n; i++) {
                tans += 1ll * dep[i] * w[i];
            }
            solve(1, 0, tans);
            cout << tans << endl;
        }
    }
    

    D - Quadratic Form(矩阵的逆)

    题意

    (X = (x_1,x_2,...,x_n)^T)(A)(n×n)的正定二次型,(b)(n×1)的列向量。

    求满足求(X^TAX le 1)((X^Tb)^2)的最大值。

    思路

    结论是(b^TA^{-1}b)

    用了使用拉格朗日乘数法,具体计算过程待补。主要是记录求矩阵的逆的高斯消元模板。

    参考

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res.txt","w",stdout)
    #define pb push_back
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    inline ll qmul(ll a, ll b, ll m) {
        ll res = 0;
        while(b) {
            if(b & 1) res = (res + a) % m;
            a = (a << 1) % m;
            b = b >> 1;
        }
        return res;
    }
    inline ll qpow(ll a, ll b, ll m) {
        ll res = 1;
        while(b) {
            if(b & 1) res = (res * a) % m;
            a = (a * a) % m;
            b = b >> 1;
        }
        return res;
    }
    
    #define INF 0x3f3f3f3f
    
    const int N = 3e2 + 10;
    const ll M = 998244353;
    const double eps = 1e-5;
    
    ll arr[N][N << 1];
    ll b[N];
    
    bool Gauss(ll a[][N << 1], int n) { //高斯消元求矩阵的逆
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                a[i][j + n] = 0;
            }
            a[i][i + n] = 1;
        }
        for(int i = 1; i <= n; i++) {
            int r = i;
            for(int j = i + 1; j <= n; j++) {
                if(a[j][i] > a[r][i]) r = j;
            }
            if(r != i) swap(a[i], a[r]);
            if(!a[i][i]) return false;
            ll inv = qpow(a[i][i], M - 2, M);
            for(int j = 1; j <= n; j++) {
                if(j == i) continue;
                ll da = a[j][i] * inv % M; //a[j][i]可能会被更新,所以要先保存
                for(int k = i; k <= (n << 1); k++) {
                    ll t = a[i][k] * da % M;
                    a[j][k] = ((a[j][k] - t) % M + M) % M;
                }
            }
            for(int j = i; j <= (n << 1); j++) a[i][j] = a[i][j] * inv % M;
        }
        return true;
    }
    
    int main() {
        IOS;
        int n;
        while(cin >> n) {
            memset(arr, 0, sizeof arr);
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++) {
                    cin >> arr[i][j];
                }
            }
            Gauss(arr, n);
            for(int i = 1; i <= n; i++) {
                cin >> b[i];
    
            }
            ll ans = 0;
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= n; j++) {
                        ans += b[i] * arr[i][j + n] % M * b[j] % M;
                        ans %= M;
                }
            }
            cout << (ans % M + M) % M << endl;
        }
    }
    

    F - Infinite String Comparision

    题意

    有字符串a,b。比较无限字符串aaa...和bbb...的大小。

    思路

    标程用了周期引理

    (若p,q是字符串s的循环节长度,则有p+q-gcd(p,q)le |s|,且gcd(p,q)也是s的一个循环节长度。)

    个人理解是(p+q-gcd(p,q)le |s|)划定了上界,第一个不相同的字符一定出现在前(p+q-gcd(p,q))的范围内。因此只需比较前(p+q-gcd(p,q))个字符。

    代码略

    周期引理证明

    另一个解法

    I - 1 or 2 (一般图匹配)

    题意

    给定一个有n个点的图,每个点有点权(d_i),代表第i个点要有(d_i)个度。问是否可以从图中删除一些边使得满足条件。

    思路

    一般图匹配模板题。

    • (d_i=1),从i连边到它所有相邻的点。
    • (d_i=2),若相邻的点的d值为2的点大于1,则连边到它所有相邻的点,否则不连d值为2的点。

    然后带花树跑一般图匹配看看是否完美匹配即可。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <vector>
    #include <cstring>
    #include <string>
    #include <deque>
    #include <cmath>
    #include <iomanip>
    #include <cctype>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res.txt","w",stdout)
    #define pb push_back
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N)
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 5e2 + 10;
    const double eps = 1e5;
     
    struct edge {
        int nt, ne;
    }ed[N * N];
    int head[N];
    int si = 0;
    int pre[N];
    int dfn[N], match[N], vis[N];
    int fa[N];
    int cnt;
     
    void init() {
        memset(head, 0, sizeof head);
        memset(match, 0, sizeof match);
        si = 0;
     
    }
     
    void Add(int u, int v) {
        si++;
        ed[si] = edge{v, head[u]};
        head[u] = si;
    }
     
    void add(int u, int v) {
        Add(u, v);
        Add(v, u);
    }
     
    int find(int x) {
        if(fa[x] == x) return x;
        return fa[x] = find(fa[x]);
    }
     
     
    int lca(int u, int v) {
        cnt++;
        while(1) {
            swap(u, v);
            if(u) { //wow!!不加直接T,有可能其中一个比另一个浅,变成0了。
                u = find(u);
                if(dfn[u] == cnt) return u;
                else {
                    dfn[u] = cnt;
                    u = pre[match[u]];
                }
            }
        }
    }
     
    void shrink(int u, int v, int rt, queue<int> &q) {
        while(find(u) != rt) {
            pre[u] = v;
            v = match[u];
            if(vis[v] == 2) {
                vis[v] = 1;
                q.push(v);
            }
            if(find(u) == u) fa[u] = rt;
            if(find(v) == v) fa[v] = rt;
            u = pre[v];
        }
    }
     
    bool aug(int s, int n) {
        for(int i = 1; i <= n; i++) fa[i] = i;
        memset(vis, 0, sizeof vis);
        memset(pre, 0, sizeof pre);
        vis[s] = 1;
        queue<int> q;
        q.push(s);
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            for(int e = head[u]; e; e = ed[e].ne) {
                int v = ed[e].nt;
                if(vis[v] == 2 || find(u) == find(v)) continue;
                if(!vis[v]) {
                    vis[v] = 2;
                    pre[v] = u;
                    if(!match[v]) {
                        int up;
                        for(int cur = v; cur; cur = up) {
                            int bp = pre[cur];
                            up = match[bp];
                            match[cur] = bp;
                            match[bp] = cur;
                        }
                        return true;
                    }
                    vis[match[v]] = 1;
                    q.push(match[v]);
                } else {
                    int rt = lca(u, v);
                    shrink(u, v, rt, q);
                    shrink(v, u, rt, q);
                }
            }
        }
        return false;
    }
     
    int d[N];
    vector<int> np[N];
     
    int main() {
        //IOS;
        int n, m;
        while(cin >> n >> m) {
            init();
            for(int i = 1; i <= n; i++) {
                cin >> d[i];
                np[i].clear();
            }
            for(int i = 1; i <= m; i++) {
                int u, v;
                cin >> u >> v;
                np[u].push_back(v);
                np[v].push_back(u);
                add(u, v);
            }
            for(int i = 1; i<= n; i++) {
                if(d[i] == 2) {
                    int dcnt = 0;
                    n++;
                    for(auto nt : np[i]) {
                        if(d[nt] == 2) {
                            dcnt++;
                        } else {
                            add(n, nt);
                        }
                    }
                    if(dcnt > 1) {
                        for(auto nt : np[i]) {
                            if(d[nt] == 2) {
                                add(n, nt);
                            }
                        }
                    }
                }
            }
            int ans = 0;
            for(int i = 1; i <= n; i++) {
                if(!match[i]) {
                    if(aug(i, n)) ans++;
                }
            }
            //cout << n << " " << ans << endl;
            if(ans == n / 2) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
    }
    
  • 相关阅读:
    【java】工厂模式Factory,利用反射改进
    【java】反射简单示例
    【java】java.util.regex.Pattern和java.util.regex.Matcher简单示例
    【java】实现一个简单的正则:判断一个字符串是否全由数字组成
    【java】实现Interface java.lang.Comparable<T>接口的int compareTo(T o)方法实现对象数组或链表或集合的排序,和挽救式对象比较器Interface java.util.Comparator<T>
    【java】java.util.Arrays类常用方法
    【java】Date与String之间的转换及Calendar类:java.text.SimpleDateFormat、public Date parse(String source) throws ParseException和public final String format(Date date)
    【java】获取当前日期时间:java.util.Date
    44-python-三维画图
    43-python-自己的词典
  • 原文地址:https://www.cnblogs.com/limil/p/13436091.html
Copyright © 2020-2023  润新知