2019牛客暑期多校训练营(第五场)A、B、G、H

A - digits 2

题意:

链接:https://ac.nowcoder.com/acm/contest/885/A

给你一个数 n (1 <= n <= 100) ,让你输出一个数 x ,x 满足两个条件 :   1 . x 是 n 的倍数,2 . x 的每一位数字加起来的和也是 n 的倍数。

解题思路:

暴力打表到70多就比较满了,所以肯定是构造,怎么构造呢? 首先先满足第一个条件,用很多 n 拼接来构造,比如 n = 15 。可以用 好多15 如: 1515151515  这个就满足那两个条件,由于用 n 来拼接的肯定满足是 n 的倍数,然后不断拼接直到每一位的和也是 n 的倍数就可以了。

AC代码:

#include
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<

B - generator 1

题意:

链接:https://ac.nowcoder.com/acm/contest/885/B

已知:x_i = a \times x_{i - 1} + b \times x_{i - 2} \ \ (i \ge 2)  给你 n,MOD,求 x_n

解题思路:

有递推方程显然用矩阵快速幂,但是因为 n 特别大,似乎使用十进制加速的快速幂才可以过。

AC代码:

#include
#define up(i, x, y) for(ll i = x; i <= y; i++)
#define down(i, x, y) for(ll i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<= 1; i--)
    {
        ll t = str[i] - '0';
        for(ll i = 1; i <= t; i++)
        {
            M = M * base;
        }
        base = base * base;
        tmp = base;
        base = base * base;
        base = base * base;
        base = base * tmp;
    }
    printf("%lld\n", (M.A[2][1] * x1 + M.A[2][2] * x0) % mod);
}

G - subsequence 1

题意:

给你一个a串,一个b串,让你求a串中有多少个子序列换成十进制数比b串换成十进制数大。3000 >= |a| >= |b| >= 1 .

解题思路:

分两种情况讨论
第一种就是当 a 串子序列的长度大于 b 串时,这时候一定满足条件,但是注意不能有前0 ,这时候就枚举 a 序列的每一位,然后只要非0,就把后面的算下组合数加起来就行了。
第二种就是当 a 串子序列的长度等于 b 串时,这时候需要dfs搜索了,但是会超时,把dfs改成记忆化就可以过了,注意求组合数要打表。

AC代码:

#include
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["< n || j > m) return 0;

    if(dp[i][j] != -1) return dp[i][j];

    if(a[i] > b[j])
    {
        if(n - i >= m - j)
            return dp[i][j] = (dfs(i + 1, j) + C[n - i][m - j]) % mod;// % mod;
        return dp[i][j] = dfs(i + 1, j);

    }
    else if(a[i] == b[j])
    {
        return dp[i][j] = (dfs(i + 1, j + 1) + dfs(i + 1, j)) % mod;
    }
    else
    {
        return dp[i][j] = dfs(i + 1, j);
    }
}
int main()
{
    init();
    int T; scanf("%d", &T); while(T--)
    {
        
        scanf("%d %d", &n, &m);
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= m; j++)
                dp[i][j]=  -1;
        scanf("%s", a + 1);
        scanf("%s", b + 1);
        ans = dfs(1, 1);
        for(int i = 1; i <= n - m + 1; i++)
        {
            if(a[i] != '0')
                for(int j = m; j <= n - i; j++)
                    ans = (ans + C[n - i][j]) % mod;
        }
        printf("%lld\n", ans);
    }
}

H - subsequence 2

题意:

链接:https://ac.nowcoder.com/acm/contest/885/H

让你猜测一个长度为 n 的字符串是什么。然后会给你 m * (m - 1) / 2 条提示。并且这个字符串是由前 m 个小写字母组成。每一个提示中会先给你两个不同的小写字符,然后再给你一个len,代表下一个要给你的字符串的长度。然后再给你一个长度为 len 的字符串,这个字符串是把真正的 长度 为 n 的字符串中的那两个小写字符按照源字符串中的顺序抽取出来的。0 <= len <= n ,当取0的时候也说明源字符串没有这俩字符。

解题思路:

因为是按照初始字符串中的顺序提取出来的,那么就给每一个字符一个编号,然后按照给的顺序连有向边,保证字符之前的前后关系,然后就可以建立一个有向图了。对于这个有向图跑拓扑排序,如果无法跑完,说明有换前后提示有矛盾输出 -1。如果可以跑完就直接是答案了。需要注意的是:如果他给你的提示中的字符不够n个,自己需要补充上,并且不能使用他提示中给的,因为他提示中给的肯定是已经出现的了,数目也是固定的,不可以修改。同时需要注意要添加前 m 个小写字母,题意要求。
在给每个字符编号时,可以设一个 id[ i ][ j ] 来确定编号,意思是:字母 i 在出现的第 j 次的编号是 id[ i ][ j ] ,这样编号就很方便了,具体细节可以参考代码。

AC代码:

#include
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["< vec[maxn];
map mp;
char s[50];
char str[maxn];
int du[maxn];
int mp2[30];
int pd[30];

int main()
{
    int cnt = 0;
    int m2;
    int n, m; scanf("%d %d", &n, &m);
    m2 = m * (m - 1) / 2;
    int len;
    memset(pd, -1, sizeof(pd));
    int cuo = 0;
    while(m2 --)
    {
        scanf("%s%d",s, &len);

        for(int i = 0; s[i] ;i++)
        {
            mp2[ s[i] - 'a' ] = 1; // 标记是否出现过,出现过就不可以使用该字母补
            if(s[i] -'a' >= m)
            {
                cuo = 1;
                goto stop;
            }
        }
        
        if(len == 0) continue;

        scanf("%s", str + 1);
        memset(used, 0, sizeof(used));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= len; i++)
        {
            int t = str[i] - 'a';
            vis[t]++;
            if(id[t][vis[t]] == 0)
            {
                id[t][vis[t]] = ++cnt;   // 编号
                mp[cnt] = 'a' + t;
            }
            used[i] = id[t][vis[t]];  // 保存前后关系
        }
        for(int i = 1; i < len; i++)
        {
            vec[ used[i] ].push_back( used[i + 1] );  // 建图
            du[used[i + 1]]++;
        }
        stop : {};
    }

    if(cuo)
    {
        puts("-1");
        return 0;
    }

    if(cnt < n)  // 如果提供的个数不足 n 个,那么就补上,补的必须是未出现的而且要小于 m
    {
        for(int i = 0; i < m; i++)
        {
            if(mp2[i] == 0)
            {
                int tmp = n - cnt;
                for(int j = 1; j <= tmp; j++)
                {
                    ++cnt;
                    mp[cnt] = 'a' + i;
                }
                break;
            }
        }
    }
    if(cnt < n) // 不能补
    {
        printf("-1\n");
        return 0;
    }

    queue que;

    for(int i = 1; i <= n; i++)
    {
        if( du[i] == 0 )
        {
            que.push(i);
        }
    }

    string ans = "";
    int f = 0;
    while(!que.empty())
    {
        int t = que.front(); que.pop();

        ans += mp[t];
        for(int i = 0; i < vec[t].size(); i++)
        {
            int v = vec[t][i];
            du[v]--;
            if(du[v] == 0) que.push(v);
        }
    }

    if(ans.length() != n) puts("-1"); // 矛盾,拓扑没跑完,肯定不成立
    else cout <

 

 

 

 

 

 

 

你可能感兴趣的:(ACM,2019牛客暑期多校训练营,其它)