[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('\n');
        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>1\Rightarrow dp[i][j](j\leq K)=\sum (dp[i-1][k] \mid j\times k\leq K,k\leq K)\)
2.\(i=1\Rightarrow dp[i][j](1\leq j\leq 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\n",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\n",ans[i][0],ans[i][1]);
    return 0;
}

posted @ 2017-08-20 18:17  free-loop  阅读(264)  评论(2编辑  收藏  举报