• 牛客练习赛#70 题解


    A.重新排列 #尺取法

    题目链接

    题意

    给定字符串,你需要将该字符串中的某个子串重新排列,使得该子串变为"puleyaknoi"。现要你求出最短的这类子串的长度。

    分析

    因为子串要求连续性,“连续性”和“多个满足条件中选择最优的”,可以联想到尺取法。

    怎么判断是否满足条件?由于子串能够重新排列,我们只需要统计相应区间中模式串对应字符的数量即可。

    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <stack>
    #include <queue>
    #include <map>
    #include <vector>
    #include <deque>
    #include <cmath>
    #include <algorithm>
    #include <unordered_map>
    using namespace std;
    typedef long long ll;
    string t = {"puleyaknoi"}, tmp;
    int vis[300];
    int q, cnt = 0;
    void Cal(char cur){
        for (int j = 0; j < t.length(); j++){
            if(cur == t[j]){
                if(vis[cur] == 0) cnt++;
                vis[cur]++;
                break;
            }
        }
    }
    int main(){
        scanf("%d", &q);
        while(q--){
            cin >> tmp;
            int len = tmp.length(), hi = -1;
            int minlen = 0x3f3f3f3f;
            memset(vis, 0, sizeof(vis));
            cnt = 0;
            for (int lo = 0; lo <= len - t.length(); lo++){
                while(hi < len && cnt < t.length()){
                    hi++;
                    Cal(tmp[hi]); //计入新元素
                }
                if(hi >= len) break; 
                if (cnt >= (int)t.length() && hi - lo + 1 < minlen) //更新长度
                    minlen = hi - lo + 1;
                if(vis[tmp[lo]] >= 1){ //排除末元素
                    vis[tmp[lo]]--;
                    if(vis[tmp[lo]] == 0) cnt--;
                }
            }
            if(minlen < 0x3f3f3f3f) printf("%d
    ", minlen);
            else printf("-1
    ");
        }
        return 0;
    }
    

    B.拼凑 #线性DP

    题目链接

    题意

    给定字符串,你需要从该字符串中找出一子序列,使得该子序列为"puleyaknoi"。现要你求出最短的这类子串的长度。

    分析

    因为子序列是原串中一部分字符(不一定是连续的)按原有次序排列而得的序列。碰到“子序列”相关问题,往往从DP方向考虑。

    定义(dp[i][j])(i)表示主串的下标(i)(j)表示模式串下标(j),当(j==模式串长度)时,(dp)数组记录最短子序列长度。其转移方程在代码中有注释。

    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <stack>
    #include <queue>
    #include <map>
    #include <vector>
    #include <deque>
    #include <cmath>
    #include <algorithm>
    #include <unordered_map>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e5 + 5;
    string s = {"puleyaknoi"}, tmp;
    int dp[MAXN][12], q;
    int main(){
        scanf("%d", &q);
        while(q--){
            cin >> tmp;
            int n = tmp.length(), m = s.length();
            memset(dp, 0x3f, sizeof(dp));
            for (int i = 1; i <= n; i++) dp[i][0] = 0;
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= min(i, m); j++){
                    //判断是否能从主串起点i-1转移过来
                    dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1);
                    if(tmp[i - 1] == s[j - 1]) //说明当前字符匹配
                        dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);
                        //判断是否能从主串起点i-1、模式串起点j-1转移过来
                }
            }
            int ans = 0x3f3f3f3f;
            for (int i = m; i <= n; i++) ans = min(ans, dp[i][m]); 
            printf("%d
    ", ans < 0x3f3f3f3f ? ans : -1);
        }
    }
    

    C.Mu函数 #质因数分解 #模拟 #打表观察性质

    题目链接

    题意

    定义函数(mu(x)),满足:

    [mu(x) = egin{cases} 1 &,x = 1 \ (-1)^a &,x无平方因数且x=prod_{i=1}^a{p_i},p_i为质数)\ 0 &,x含大于1的平方数因数 end{cases} ]

    设函数(f(x)=x+mu(x)),给定(n,k),求(f(n))(k(kleq10^{18}))次迭代

    分析

    这题Wa了十多发,看到好多题解需要莫比乌斯反演(?),没学过。

    参考了@为神的思路,我们要解决几个问题:

    1. 如何判断(x)是否存在平方数因数?直接枚举即可。
    2. 如何对(x)质因数分解?先用线性筛,记录每个数是否为质数的同时,给每一个合数存下相应的最小质因子。接下来对(x)不断地用其最小质因子去除,直到商为质数即可统计出原来的(x)有多少个质因子。
    3. 如何处理(10^{18})(k)?如此数据规模,暗示我们要从中找出算法提前终止的条件与有规律性的结果。
      • 如果(x)满足(mu(x))的条件三,说明接下来的增量都为(0)(f(x))始终不变,直接退出;
      • 如果(x)满足(mu(x))的条件二,实际上就是对(x)自减或者自增。我们通过小范围打表发现,到达一定的(k)时,(mu(x))会在(-1)(+1)之间周期变化,也就说上一增量和下一增量之和就是(0),由此我们通过周期规律,预先知道未来的结果。
    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <stack>
    #include <queue>
    #include <map>
    #include <vector>
    #include <deque>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e7 + 5;
    int q, n, prim[MAXN], notPrim[MAXN]; //notPrim[x]存储x的最小质因子
    ll k;
    void getPrime(){
        int cnt = 0;
        for(ll i = 2; i <= (ll)1e7+3; i++){
            if(notPrim[i] == 0) //i并非合数 即 i是质数
                prim[++cnt] = i;
            for(int j = 1; i * (ll)prim[j] <= (ll)1e7+3 && j <= cnt; j++){
                notPrim[i * prim[j]] = prim[j];
                if(i % prim[j] == 0) break;
            }
        }
    }
    int res;
    bool Judge(int x){
        res = 0;
        for(int i = 2; i * i <= x; i++)
            if(x % (i * i) == 0) return false; //满足条件三
        while(notPrim[x] > 0){ //说明x此时仍不是质数
            x = x / notPrim[x];
            res++;
        }
        if(x != 1) res++;
        return true;
    }
    int main(){
        scanf("%d", &q);
        getPrime();
        while(q--){
            scanf("%d%lld", &n, &k);
            int last = 0, diff = 0;
            while(k--){
                last = diff;
                if(Judge(n)){//满足条件二
                    diff = 1 - 2 * (res % 2);
                    n += diff;
                    if(diff + last == 0){
                        if(k % 2 != 0) n += last;
                        break;
                    }
                }
                else break; //满足条件三
            }
            printf("%d
    ", n);
        }
    }
    

    D.数树 #树的性质 #STL

    题目链接

    题意

    现有三种操作:1. 添加一条边;2. 切断一条边;3.询问现有多少个规模不为1的树。

    操作中有可能连出重边,或者切断根本不存在的边,可以忽略。

    分析

    由于每一棵树满足"边数+1=节点数",我们可以用总边数-节点数,便能求出森林中树的数量。但注意,这些树都是由度数不为1的节点组成的。另外,由于会出现重边,或者删除不存在的边,我们需要用哈希表去记录边的数量。又因为节点编号达到(10^8),需要用到(map)(pair)来维护与记录。

    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <stack>
    #include <queue>
    #include <map>
    #include <vector>
    #include <deque>
    #include <cmath>
    #include <algorithm>
    #include <unordered_map>
    #include <set>
    using namespace std;
    typedef long long ll;
    const int MAXN = 2e5 + 5;
    map< pair<int, int>, int> Evis;
    map<int, int> deg;
    int ans = 0, n, cnt, tot;
    int main() {
        scanf("%d", &n);
        while (n--) {
            int opt, u, v;
            scanf("%d", &opt);
            if (opt == 1) {
                scanf("%d%d", &u, &v);
                if(Evis.count({u, v})) continue; //忽略操作
                Evis[{u, v}] = Evis[{v, u}] = 1;
                cnt++; //边数+1
                deg[u]++; //度数+1
                deg[v]++; 
                if(deg[u] == 1) tot++; //度数不为1的顶点数+1
                if(deg[v] == 1) tot++;
            }
            else if (opt == 2) {
                scanf("%d%d", &u, &v);
                if(!Evis.count({u, v})) continue; //忽略操作
                Evis.erase({u, v});
                Evis.erase({v, u});
                cnt--; //边数-1
                deg[u]--; //度数-1
                deg[v]--;
                if(deg[u] == 0) tot--; 
                if(deg[v] == 0) tot--; //tot始终记录度数不为1的顶点数
            }
            else if (opt == 3) {
                printf("%d
    ", tot - cnt); //不成一个点的树的条数 = 度数不为1的顶点数 - 边数
            }
        }
    }
    
  • 相关阅读:
    md5
    表空间
    create_index
    非额度合同和额度合同
    如何在linux中查找python安装包的路径
    Golang中的SingleFlight与CyclicBarrier
    linux安装protoc
    protobuf 的优缺点
    Xshell 连接 VirtualBox
    限制 input 输入框只能输入纯数字
  • 原文地址:https://www.cnblogs.com/J-StrawHat/p/13736588.html
Copyright © 2020-2023  润新知