本文同步发布于:https://goodcoder666.github.io/post/abc242/
ABC242 C~E
- [C - 1111gal password](https://atcoder.jp/contests/abc242/tasks/abc242_c)
-
- 题目大意
- 输入格式
- 输出格式
- 样例
- 分析
- 代码
- [D - ABC Transform](https://atcoder.jp/contests/abc242/tasks/abc242_d)
-
- 题目大意
- 输入格式
- 样例
-
- 样例输入1
- 样例输出1
- 样例输入2
- 样例输出2
- 分析
- 代码
-
- 代码1(标准)
- 代码2(优化)
- [E - (?x?)](https://atcoder.jp/contests/abc242/tasks/abc242_e)
-
- 题目大意
- 分析
- 代码
C - 1111gal password 题目大意 给定正整数 N N N,求符合下列条件的整数 X X X的个数,对 998244353 998244353 998244353取模:
- X X X是 N N N位的正整数
- X X X的每一位数都在 [ 1 , 9 ] [1,9] [1,9]之间(0不行);
- X X X的相邻两位数之差的绝对值不超过 1 1 1。
输入格式 N N N
输出格式 输出答案。
样例
N N N | 输出 |
---|---|
4 4 4 | 203 203 203 |
2 2 2 | 25 25 25 |
1000000 1000000 1000000 | 248860093 248860093 248860093 |
但是,由于每一位会被上一位所限制,所以我们很容易想到使用 DP \text{DP} DP求解。
令 f ( i , j ) = X f(i,j)=X f(i,j)=X的第 i i i位上出现 j j j的可能数,易得:
f ( i , j ) = { 1 ( i = 1 ) f ( i ? 1 , 1 ) + f ( i ? 1 , 2 ) ( j = 1 ) f ( i ? 1 , 8 ) + f ( i ? 1 , 9 ) ( j = 9 ) f ( i ? 1 , j ? 1 ) + f ( i ? 1 , j ) + f ( i ? 1 , j + 1 ) ( i > 1 , 2 ≤ j ≤ 8 ) f(i,j)=\begin{cases} 1&(i=1)\\ f(i-1,1)+f(i-1,2)&(j=1)\\ f(i-1,8)+f(i-1,9)&(j=9)\\ f(i-1,j-1)+f(i-1,j)+f(i-1,j+1)&(i>1,2\le j\le8) \end{cases} f(i,j)=??????????1f(i?1,1)+f(i?1,2)f(i?1,8)+f(i?1,9)f(i?1,j?1)+f(i?1,j)+f(i?1,j+1)?(i=1)(j=1)(j=9)(i>1,2≤j≤8)?
因此,直接输出 ∑ i = 1 9 f ( n , i ) \sum\limits_{i=1}^9f(n,i) i=1∑9?f(n,i)即可。
代码 本代码运用了滚动表的优化,当然也可以直接开 N × 9 N\times9 N×9大小的数组,但这样会导致内存占用大,不建议使用。
#include
#define MOD 998244353
using namespace std;
inline void mod(int& x)
{
if(x >= MOD) x -= MOD;
}int dp[9], ldp[9];
int main()
{
int n;
scanf("%d", &n);
for(int i=0;
i<9;
i++)
dp[i] = 1;
while(--n)
{
for(int i=0;
i<9;
i++)
ldp[i] = dp[i];
mod(dp[0] += dp[1]), mod(dp[8] += dp[7]);
for(int i=1;
i<8;
i++)
mod(dp[i] += ldp[i - 1]),
mod(dp[i] += ldp[i + 1]);
}
int ans = 0;
for(int i=0;
i<9;
i++)
mod(ans += dp[i]);
printf("%d\n", ans);
return 0;
}
D - ABC Transform 题目大意 给定由
A
、B
、C
组成的字符串 S S S。令 S 0 = S S_0=S S0?=S, S i = S i ? 1 S_i=S_{i-1} Si?=Si?1?将A
、B
、C
分别替换为BC
、CA
、AB
的新字符串。回答 Q Q Q个查询,第 i i i个查询的问题如下:
- 求 S t i S_{t_i} Sti??的第 k i k_i ki?个字母。
1 ≤ Q ≤ 1 0 5 1\le Q\le 10^5 1≤Q≤105
1 ≤ t i ≤ 1 0 18 1\le t_i\le 10^{18} 1≤ti?≤1018
1 ≤ k i ≤ m i n ( 1 0 18 , S t i 1\le k_i\le min(10^{18},S_{t_i} 1≤ki?≤min(1018,Sti??的长度 ) ) )
输入格式 S S S
Q Q Q
t 1k 1 t_1~k_1 t1? k1?
? \vdots ?
t Qk Q t_Q~k_Q tQ? kQ?
样例 样例输入1
ABC
4
0 1
1 1
1 3
1 6
样例输出1
A
B
C
B
- S 0 =S_0=~ S0?=
ABC
- S 1 =S_1=~ S1?=
AABCB
CBBAACCCCC
5
57530144230160008 659279164847814847
29622990657296329 861239705300265164
509705228051901259 994708708957785197
176678501072691541 655134104344481648
827291290937314275 407121144297426665
样例输出2
A
A
C
A
A
注意小心整数溢出问题。
分析 令 f ( t , k ) = ( S 0 f(t,k)=(S_0 f(t,k)=(S0?为
AAA..
时 S t S_t St?的第 k k k个字母,其中A
、B
、C
分别对应 0 , 1 , 2 0,1,2 0,1,2且 k k k从 0 0 0开始 ) ) ),则通过找规律可得:f ( t , k ) = { 0 ( t = 0 ) g ( 0 , t ) ( k = 0 ) g ( f ( t ? 1 , ? k 2 ? ) , ( k ? m o d ? 2 ) + 1 ) ( t > 0 , k > 0 ) f(t,k)=\begin{cases} 0 & (t=0)\\ g(0,t) & (k=0)\\ g(f(t-1,\lfloor\frac k2\rfloor),(k\bmod2)+1) & (t>0,k>0) \end{cases} f(t,k)=??????0g(0,t)g(f(t?1,?2k??),(kmod2)+1)?(t=0)(k=0)(t>0,k>0)?
其中 g ( c , x ) g(c,x) g(c,x)为字符 c c c在
A,B,C,A,...
这个环中 c c c后面的第 x x x个字符,即 g ( c , x ) = ( c + x ) ? m o d ? 3 g(c,x)=(c+x)\bmod3 g(c,x)=(c+x)mod3。因此,我们只要求出 x x x在 S S S的哪个字符分解后的结果中,再计算 f f f即可。
答案为 a n s = g ( f ( t , ( k ? 1 ) ? m o d ? 2 t ) , S ? k ? 1 2 t ? ) \mathrm{ans}=g(f(t,(k-1)\bmod2^t),S_{\lfloor\frac {k-1}{2t}\rfloor}) ans=g(f(t,(k?1)mod2t),S?2tk?1???)。
代码 以下两种示范代码均使用非递归形式,当然也可使用递归形式。
代码1(标准)
#include
using namespace std;
char s[100005];
int main()
{
scanf("%s", s);
int q;
scanf("%d", &q);
while(q--)
{
long long t, k;
scanf("%lld%lld", &t, &k);
k --;
int x = s[t < 64? k >> t: 0] - 'A';
// 防止t太大导致RE
while(t > 0 && k > 0)
{
x = (x + int(k & 1LL) + 1) % 3;
k >>= 1LL, t --;
}
putchar((t + x) % 3 + 'A');
putchar('\n');
}
return 0;
}
代码2(优化)
#include
using namespace std;
char s[100005];
int main()
{
scanf("%s", s);
int q;
scanf("%d", &q);
while(q--)
{
long long t, k;
scanf("%lld%lld", &t, &k);
k --;
int c = 0;
if(t < 64)
{
c = s[k >> t] - 'A';
k &= (1LL << t) - 1LL;
}
else c = s[0] - 'A';
for(c+=t%3;
k>0;
k&=k-1) c ++;
putchar(c % 3 + 'A');
putchar('\n');
}
return 0;
}
E - (?x?) 题目大意 对于 T T T个测试点,分别解决下列问题:
给定整数 N N N和字符串 S S S,求合法字符串 X X X的个数,使其符合下列条件:
- ∣ X ∣ = N |X|=N ∣X∣=N
- X X X由大写英文字母组成,是一个回文串
- 按字典序, X ≤ S X\le S X≤S
1 ≤ N ≤ 1 0 6 1\le N\le 10^6 1≤N≤106
1 ≤ ∑ N ≤ 1 0 6 1\le \sum N\le 10^6 1≤∑N≤106
∣ S ∣ = N |S|=N ∣S∣=N且由大写英文字母组成。
分析 显然,通过 X X X的前 ? N 2 ? \lceil\frac N2\rceil ?2N??个字符就可以确定唯一的 X X X。下面,我们以
ABCDE
为例:ABCDE
的前 ? N 2 ? \lceil\frac N2\rceil ?2N??个字符分别为ABC
- 字典序小于
ABC
的字符串有 28 28 28个(可看作一个 26 26 26进制数来计算) - 判断
ABCBA
是否可行,与ABCDE
比较 - 可行,答案增加 1 1 1得到 29 29 29
代码
#include
#define maxn 1000005
#define MOD 998244353
using namespace std;
using LL = long long;
char s[maxn];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d%s", &n, s);
long long x = 0LL;
int j = n - 1 >> 1;
for(int i=0;
i<=j;
i++)
(x = x * 26LL + s[i] - 'A') %= MOD;
bool ok = true;
while(j >= 0)
{
if(s[j] < s[n - 1 - j]) break;
if(s[j] > s[n - 1 - j]) { ok = false;
break;
}
j --;
}
if(ok && ++x == MOD) x -= MOD;
printf("%lld\n", x);
}
return 0;
}
推荐阅读
- Leetcode|leetcode-蜡烛之间的盘子(经典空换时)
- python|基于移动最小二乘(MLS)的图像扭曲刚性变形python实现
- 数据结构与算法|【干货】数据结构与算法该如何正确学习((书籍\视频\网站都推荐了))
- 学习路线|学习计算机基础有什么推荐的书和视频()
- C语言基础|【Visual Studio 2019】 实用调试技巧,学会了都说好
- C语言进阶|【详解C语言指针】我真的让C指针给我唱征服了~乌拉
- 笔记|【算法求职】转行算法岗,如何准备()
- C语言|C语言两种方法计算一个数所有位上的数的总和
- 学习经验分享|蓝桥杯2020第十一届C语言B组省赛习题题解