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<bits/stdc++.h>
#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"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false),cin.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

int fun(int x)
{
    int ans = 0;
    while(x)
    {
        ans += x % 10;
        x /= 10;
    }
    return ans;
}

int main()
{
    int T; scanf("%d", &T); while(T--)
    {
        int n; scanf("%d", &n);
        int num = fun(n);
        int sum = num;
        while(sum % n != 0)
        {
            sum += num;
        }
        for(int i = 1; i <= sum / num; i++)
        {
            printf("%d", n);
        }
        puts("");
    }
}

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<bits/stdc++.h>
#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"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false),cin.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const ll maxn = 1e6 + 7;
const double pi = acos(-1);
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

ll x0, x1, a, b;
char str[maxn];
ll mod;

struct Matrix
{
    ll n, m;
    ll A[10][10];

    inline void init(ll nn, ll mm)
    {
        n = nn, m = mm;
        for(ll i = 1; i <= n; i++)
        {
            for(ll j = 1; j <= m; j++)
            {
                A[i][j] = 0;
            }
        }
    }

    inline Matrix operator * (const Matrix &t)const
    {
        Matrix res;
        res.init(n, t.m);
        for(ll i = 1; i <= n; i++)
            for(ll j = 1; j <= t.m; j++)
                for(ll k = 1; k <= m; k++)
                    res.A[i][j] = (res.A[i][j] + A[i][k] * t.A[k][j]) % mod;
        return res;
    }

};

int main()
{
    scanf("%lld %lld %lld %lld", &x0, &x1, &a, &b);
    scanf("%s %lld", str + 1, &mod);
    ll len = strlen(str + 1);
    Matrix M; M.init(2, 2);
    Matrix base; base.init(2, 2);
    Matrix tmp;tmp.init(2, 2);
    base.A[1][1] = a, base.A[1][2] = b;
    base.A[2][1] = 1, base.A[2][2] = 0;
    M.A[1][1] = 1, M.A[1][2] = 0;
    M.A[2][1] = 0, M.A[2][2] = 1;
    for(ll i = len; i >= 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<bits/stdc++.h>
#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"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false),cin.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int maxn = 3000 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

ll mod = 998244353;
ll C[maxn][maxn];
char a[maxn], b[maxn];
int n, m;
ll ans = 0;

void init()
{
    for(int i = 0; i <= 3000; i++)
    {
        C[i][0] = 1;
        for(int j = 1; j <= i; j++)
        {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
}

ll dp[maxn][maxn];

ll dfs(int i, int j)
{
    if(i > 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<bits/stdc++.h>
#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"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false),cin.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

int id[30][maxn];
int vis[30];
int used[maxn];
vector<int> vec[maxn];
map<int, char> 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<int> 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 <<ans <<'\n';

}


/*
3 2
ab 0
cd 0
*/

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值