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
已知: 给你 n,MOD,求 。
解题思路:
有递推方程显然用矩阵快速幂,但是因为 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
*/