• [Offer收割]编程练习赛23-freeloop


    A. H国的身份证号码I

    dfs裸题。
    时间复杂度(O(n^k))

    #include <bits/stdc++.h>
    #define FOR(i,a,b) for (int i=a; i<=b; ++i)
    int ans[15], n, k;
    
    void dfs(int pos, int pre){
        if (pos>n) {
            FOR(i,1,n) printf("%d",ans[i]);
            putchar('
    ');
            return ;
        }
        FOR(i,0,k) {
            if (i*pre>k) break;
            ans[pos]=i; dfs(pos+1,i);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        FOR(i,1,k) ans[1]=i, dfs(2,i);
        return 0;
    }
    

    B.合并子目录

    (/)为分隔符,对所有的文件夹进行hash后存入map,给每个文件夹的hash值分配一个id。
    以这些id和文件夹的从属关系建树,那么只要(size[i]=1)就表示这个文件夹i可以和它的子文件夹合并。
    时间复杂度(O(nlogn))

    #include <bits/stdc++.h>
    #define FOR(i,a,b) for (int i=a; i<=b; ++i)
    #define pb push_back 
    map<int,int>mp;
    map<int,bool>mark;
    int id;
    string s[N];
    vector<int> v[500000], g[500000];
    
    int main ()
    {
        int n, len, hah, p, t;
        cin>>n;
        FOR(i,1,n) {
            cin>>s[i];
            len=s[i].length(); hah=0;
            for (int j=1; j<len; ++j) {
                hah=131*hah+s[i][j];
                if (s[i][j]=='/') {
                    if (mp.find(hah)==mp.end()) {
                        mp[hah]=++id;
                        if ((t=v[i].size())) g[mp[v[i][t-1]]].pb(id);
                    }
                    v[i].pb(hah);
                }
            }
            if ((t=v[i].size())) mark[v[i][t-1]]=true;
        }
        FOR(i,1,n) {
            len=s[i].length(); p=0;
            for (int j=1; j<len; ++j) {
                if (s[i][j]=='/') {
                    if (p+1>=v[i].size()) break;
                    if (g[mp[v[i][p]]].size()==1 && mark.find(v[i][p])==mark.end()) s[i][j]='-';
                    ++p;
                }
            }
        }
        FOR(i,1,n) cout<<s[i]<<endl;
        return 0;
    }
    

    C.H国的身份证号码II

    (dp[i][j])表示前i位且第i位的数字是j的方法数。
    显然有
    1.(i>1Rightarrow dp[i][j](jleq K)=sum (dp[i-1][k] mid j imes kleq K,kleq K))
    2.(i=1Rightarrow dp[i][j](1leq jleq K)=1)
    注意到这个dp方程可以用矩阵快速幂加速。
    时间复杂度(O(10^3logn))

    #include <bits/stdc++.h>
    #define FOR(i,a,b) for (int i=a; i<=b; ++i)
    typedef long long LL;
    
    struct Matrix{LL matrix[N][N];}a, sa, unit, kk;
    LL n;
    int k;
    
    Matrix Mul(Matrix a, Matrix b) //矩阵乘法(%MOD)
    {
        Matrix c;
        FOR(i,0,9) FOR(j,0,9) {
              c.matrix[i][j]=0;
              FOR(l,0,9) c.matrix[i][j]+=(a.matrix[i][l]*b.matrix[l][j])%MOD;
              c.matrix[i][j]%=MOD;
        }
        return c;
    }
    Matrix Cal(LL exp)  //矩阵快速幂
    {
        Matrix p=a, q=unit;
        if (exp==0) return q;
        while (exp!=1) {
            if (exp&1) exp--, q=Mul(p,q);
            else exp>>=1, p=Mul(p,p);
        }
        return Mul(p,q);
    }
    void init(){
        FOR(i,0,9) unit.matrix[i][i]=1;
        FOR(i,1,min(k,9)) kk.matrix[0][i]=1;
        FOR(j,0,min(k,9)) FOR(i,0,min(k,9)) if (i*j<=k) a.matrix[i][j]=1;
    }
    int main ()
    {
        LL ans=0;
        scanf("%lld%d",&n,&k);
        init();
        sa=Cal(n-1); sa=Mul(kk,sa);
        FOR(i,0,9) ans=(ans+sa.matrix[0][i])%MOD;
        printf("%lld
    ",ans);
        return 0;
    }
    

    D.观光旅行

    图的最小生成树有一个性质,图的最小生成树上任意两点((u,v))路径里的边的最大值是原图里((u,v))路径里边的最大值最小的边。
    由于题意中边权各不相同,所以最小生成树必定唯一。
    因此对于那些不在最小生成树上的边,答案就是((0,0))
    现在的问题是,对于出现在最小生成树的每一条边((u,v)),找出一条路径((a,b)),使得边((u,v))出现在路径((a,b))上,且是路径上的边权最大值。要求(a<b)且a尽量大b尽量小。

    考虑边((u,v))了,那么a必然是与u左边的一些点,b必然是与v右边的一些点。并且这两边的点分别构成了一个集合,使得集合内的边权都小于(w(u,v))
    于是可以考虑按边权从小到大的方式枚举,用并查集维护点之间的连通性。
    由于最后的答案还有要求,我们还需要对每个点集维护一个set来查询集合内id最大值,以及集合内第一个大于x的id。
    然而或许还是会TLE,因为过程中需要合并set,考虑set的启发式合并,每次将size小的set并入size大的set内。
    这些都可以在Kruskal的过程中完成。
    时间复杂度(O(nlog^2n))

    #include <bits/stdc++.h>
    #define FOR(i,a,b) for (int i=a; i<=b; ++i)
    typedef long long LL;
    
    int n, m;
    struct Node{int u, v, w, id;}node[N<<1];
    int F[N], ans[N<<1][2];
    int ma[N], tmp[N];
    set<int> a[N];
    set<int>::iterator it;
    
    bool comp(Node a, Node b){return a.w<b.w;}
    int find(int x){return F[x]==0?x:(F[x]=find(F[x]));}
    void GB(int x, int y){
        for (it=a[x].begin(); it!=a[x].end(); ++it) a[y].insert(*it);
    }
    void Prim(){
        sort(node+1,node+m+1,comp);
        FOR(i,1,m) {
            int u=find(node[i].u), v=find(node[i].v);
            if (u==v) continue;
            if (ma[u]>ma[v]) {
                ans[node[i].id][0]=ma[v], ans[node[i].id][1]=*a[u].lower_bound(ma[v]);
            }
            else {
                ans[node[i].id][0]=ma[u], ans[node[i].id][1]=*a[v].lower_bound(ma[u]);
            }
            if (a[u].size()>a[v].size()) swap(u,v);
            ma[v]=max(ma[u],ma[v]); GB(u,v);
            F[u]=v;
        }
    }
    int main ()
    {
        scanf("%d%d",&n,&m);
        FOR(i,1,m) scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w), node[i].id=i;
        FOR(i,1,n) a[i].insert(i), ma[i]=i;
        Prim();
        FOR(i,1,m) printf("%d %d
    ",ans[i][0],ans[i][1]);
        return 0;
    }
    
    
  • 相关阅读:
    内网穿透
    canvas 满天星
    swift 获取文件的Md5值
    swift UITextView内容距离边框边距设置
    swift UITextField光标聚焦以及光标颜色修改
    swift3.0 移除当前页面的前一个页面
    swift3.0 屏幕截图并且保存到本地相册
    swift3.0 UITableView侧滑支持多选项
    swift3.0 点击UIScrollView中输入框之外的区域关闭键盘
    swift3.0 底部弹出菜单 UIAlertController的使用
  • 原文地址:https://www.cnblogs.com/lishiyao/p/7400871.html
Copyright © 2020-2023  润新知