2022年5月1日14:39:50
文章目录
A. Subtle Substring Subtraction
题目大意:
爱丽丝和鲍勃正在玩弦乐游戏。比赛将进行 t t t 轮。在每一轮中,都会有一个由小写英文字母组成的字符串s。
爱丽丝首先移动,两个玩家交替轮流。 Alice 可以删除任何偶数长度的子串(可能为空),Bob 可以从 s s s 中删除任何奇数长度的子串。
更正式地说,如果有一个字符串 s = s 1 s 2 . . . s k s=s_1s_2...s_k s=s1s2...sk,玩家可以选择一个长度为相应奇偶校验的子字符串 s l s l + 1 . . . s r − 1 s r s_ls_{l+1}...s_{r−1}s_r slsl+1...sr−1sr 并将其删除。之后,字符串将变为 s = s 1 … s l − 1 s r + 1 … s k s=s_1…s_{l−1}s_{r+1}…sk s=s1…sl−1sr+1…sk。
字符串变空后,回合结束,每位玩家计算本回合的得分。一个玩家的得分是他/她移除的所有角色的值的总和。 a 的值为 1,b 的值为 2,c 的值为 3,……,z 的值为 26。得分较高的玩家获胜。对于每一轮,确定获胜者以及获胜者和失败者得分之间的差异。假设两个玩家都打得最优化以最大化他们的分数。可以证明,平局是不可能的。
输入
输入的第一行包含一个整数
t
(
1
≤
t
≤
5
⋅
1
0
4
)
t (1≤t≤5⋅10^4)
t(1≤t≤5⋅104),表示轮数。
接下来的 t t t 行中的每一行都包含一个由小写英文字母组成的字符串 s ( 1 ≤ ∣ s ∣ ≤ 2 ⋅ 1 0 5 ) s (1≤|s|≤2⋅10^5) s(1≤∣s∣≤2⋅105),表示用于该回合的字符串。这里 ∣ s ∣ |s| ∣s∣ 表示字符串 s s s 的长度。
保证 ∣ s ∣ |s| ∣s∣ 的总和总轮次不超过 2 ⋅ 1 0 5 2⋅10^5 2⋅105。
输出
对于每一轮,打印一行包含一个字符串和一个整数。如果 Alice 赢得这一轮,则字符串必须是“Alice”。如果 Bob 赢得了回合,则字符串必须是“Bob”。整数必须是他们的分数之间的差,假设两个玩家都打得最好。
测试样例:
**input**
5
aba
abc
cba
n
codeforces
**output**
Alice 2
Alice 4
Alice 4
Bob 14
Alice 93
题解:
贪心就完了。Alice先删,所以除了n==1
Bob赢,其余Alice必赢。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
void solve(){
string s;
cin >> s;
int n = s.length();
if(n==1){//奇数个Bob一定赢
cout << "Bob " << s[0] - 'a' + 1 << "\n";
return;
}
int sum = 0;
for(auto i:s) //所有字母值之和
sum += i - 'a' + 1;
if (n % 2 == 0) //Alice把所有全删除掉
cout << "Alice " << sum << "\n";
else //Alice把前n-1或者后n-1个字母删掉
cout << "Alice " << sum - 2 * min(s[0] - 'a' + 1, s[n - 1] - 'a' + 1) << "\n";
// sum=a+b; a=sum=b; a-b=sum-2*b;
}
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
B. A Perfectly Balanced String?
题目大意:
如果对于所有可能的三元组 ( t , u , v ) (t,u,v) (t,u,v) 使得 t t t 是 s s s 的非空子串并且 u u u 和 v v v 是 s s s 中存在的字符,那么我们称一个字符串 s s s 是完全平衡的, u u u 和 v v v 在 t t t 中频率之间的差异不大于 1 1 1。
例如,字符串 “ a b a aba aba” 和 “ a b c abc abc” 是完全平衡的,但 “ a b b abb abb” 不是,因为对于三元组 (“ b b bb bb”,‘ a a a’,‘ b b b’),条件不满足。
给你一个字符串 s s s 仅由小写英文字母组成。你的任务是确定 s s s 是否完全平衡。
如果 b 可以通过从 a 的开头删除一些字符(可能为 0)和从 a 的结尾删除一些字符(可能为 0)来获得,则字符串 b 称为另一个字符串 a 的子字符串。
输入
输入的第一行包含一个整数
t
(
1
≤
t
≤
2
⋅
1
0
4
)
t (1≤t≤2⋅10^4)
t(1≤t≤2⋅104),表示测试用例的数量。
接下来的 t t t 行中的每一行都包含一个字符串 s ( 1 ≤ ∣ s ∣ ≤ 2 ⋅ 1 0 5 ) s (1≤|s|≤2⋅10^5) s(1≤∣s∣≤2⋅105),由小写英文字母组成。
保证 ∣ s ∣ |s| ∣s∣ 的总和在所有测试用例中不超过 2 ⋅ 1 0 5 2⋅10^5 2⋅105。
输出
对于每个测试用例,如果
s
s
s 是一个完美平衡的字符串,则打印“YES”,否则打印“NO”。
您可以在任何情况下打印每个字母(例如,“YES”、“Yes”、“yes”、“yEs”都将被识别为肯定答案)。
测试样例:
**input**
5
aba
abb
abc
aaaaa
abcba
**output**
YES
NO
YES
YES
NO
题解:
如果字符串是周期性的并且重复模式包含所有不同的字母,则该字符串是完全平衡的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
void solve(){
string s;
cin >> s;
int n = s.length();
set<char> c;
bool ok = true;
int k;
for(k=0;k<n;k++){
if(c.find(s[k])==c.end())
//若在set c 中找不到则 c.find(x) == c.end()
c.insert(s[k]);
else
break;
}
for(int i=k;i<n;i++){
if(s[i]!=s[i-k])
ok = false;
}
if(ok)
cout << "YES\n";
else
cout << "NO\n";
}
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
C. Palindrome Basis
题目大意:
给你一个正整数 n n n。 如果某个正整数 a a a 在颠倒其数字的顺序后保持不变,则称其为不带前导零回文的整数。 找出将 n n n 表示为正回文整数之和的不同方式的数量。 如果至少一个回文整数的频率不同,则两种方式被认为是不同的。 例如,5=4+1 和 5=3+1+1 被认为是不同的,但 5=3+1+1 和 5=1+3+1 被认为是相同的。
形式上,您需要找到总和等于 n 的正回文整数的不同多重集的数量。
由于答案可能很大,因此 模 1 0 9 + 7 10^9+7 109+7 输出。
输入
输入的第一行包含一个整数
t
(
1
≤
t
≤
1
0
4
)
t (1≤t≤10^4)
t(1≤t≤104),表示测试用例的数量。
每个测试用例包含一行输入,其中包含单个整数 n ( 1 ≤ n ≤ 4 ⋅ 1 0 4 ) n (1≤n≤4⋅10^4) n(1≤n≤4⋅104) — 所需的回文整数之和。
输出
对于每个测试用例,打印一个表示所需答案模
1
0
9
+
7
10^9+7
109+7 的整数。
测试样例:
**input**
2
5
12
**output**
7
74
笔记
对于第一个测试用例,有 7 种方法将 5 划分为正回文整数之和:
- 5=1+1+1+1+1
- 5=1+1+1+2
- 5=1+2+2
- 5=1+1+3
- 5=2+3
- 5=1+4
- 5=5
对于第二个测试用例,共有 77 种方法将 12 分区为正整数之和,但其中,分区 12=2+10、12=1+1+10 和 12=12 不是 12 的有效分区作为 正回文整数之和,因为 10 和 12 不是回文数。 因此,有 74 种方法可以将 12 划分为正回文整数的总和。
题解:
First, we need to observe that the number of palindromes less than 4 ⋅ 1 0 4 4\cdot 10^4 4⋅104 is relatively very small. The number of 5 5 5-digit palindromes are 300 300 300 (enumerate all 3 3 3-digit numbers less than 400 400 400 and append the first two digits in the reverse order). Similarly, the number of 4 4 4-digit, 3 3 3-digit, 2 2 2-digit and 1 1 1-digit palindromes are 90 90 90, 90 90 90, 9 9 9 and 9 9 9 respectively, giving a total of M = 498 M=498 M=498 palindromes.
Now, the problem can be solved just like the classical partitions problem which can be solved using Dynamic Programming.
Let d p k , m = dp_{k,m} = dpk,m= Number of ways to partition the number k k k using only the first m m m palindromes. It is not hard to see that d p k , m = d p k , m − 1 + d p k − p m , m dp_{k,m} = dp_{k,m-1} + dp_{k-p_m,m} dpk,m=dpk,m−1+dpk−pm,m where p m p_m pm denotes the m t h m^{th} mth palindrome. The first term corresponds to the partitions of k k k using only the first m − 1 m-1 m−1 palindromes and the second term corresponds to those partitions of k k k in which the m t h m^{th} mth palindrome has been used atleast once. As base cases, d p k , 1 = 1 dp_{k,1}=1 dpk,1=1 and d p 1 , m = 1 dp_{1,m}=1 dp1,m=1. The final answer for any n n n will be d p n , M dp_{n,M} dpn,M.
The time and space complexity is O ( n ⋅ M ) \mathcal{O}(n\cdot M) O(n⋅M).
首先,我们需要观察到小于 4 ⋅ 1 0 4 4\cdot 10^4 4⋅104 的回文数相对非常少。 5 5 5-digit 回文数是 300 300 300(枚举所有小于 400 400 400 的 3 3 3-digit 数字并以相反的顺序附加前两位数字)。类似地, 4 4 4-digit、 3 3 3-digit、 2 2 2-digit 和 1 1 1-digit 回文数分别为 90 90 90、 90 90 90、 9 9 9 和 9 9 9,总共有 M = 498 M=498 M=498 个回文。
现在,这个问题可以像经典的分区问题一样解决,可以使用动态编程解决。
令 d p k , m = dp_{k,m} = dpk,m= 仅使用前 m m m 回文数来划分数字 k k k 的方法数。不难看出 d p k , m = d p k , m − 1 + d p k − p m , m dp_{k,m} = dp_{k,m-1} + dp_{k-p_m,m} dpk,m=dpk,m−1+dpk−pm,m 其中 p m p_m pm 表示 m t h m^{th} mth 回文。第一项对应于仅使用第一个 m − 1 m-1 m−1 回文的 k k k 的分区,第二项对应于 k k k 的那些分区,其中 m t h m^{th} mth 回文至少被使用过一次。作为基本情况, d p k , 1 = 1 dp_{k,1}=1 dpk,1=1 和 d p 1 , m = 1 dp_{1,m}=1 dp1,m=1。任何 n n n 的最终答案都是 d p n , M dp_{n,M} dpn,M。
时间和空间复杂度为 O ( n ⋅ M ) \mathcal{O}(n\cdot M) O(n⋅M)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
const int N = 40004, M = 502;
const ll MOD = 1000000007;
ll dp[N][M];
int reverse(int n){
int r=0;
while(n>0)
{
r=r*10+n%10;
n/=10;
}
return r;
}
bool palindrome(int n){
return (reverse(n)==n);
}
void solve(){
int n;
cin >> n;
cout << dp[n][M-1] << '\n';
}
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
vector<int> palin;
palin.push_back(0);
for(int i=1;i<2*N;i++){
if(palindrome(i))
palin.push_back(i);
}
for(int i=1;i<M;i++)
dp[0][i]=1;
for(int i=1;i<N;i++){
dp[i][0]=0;
for(int j=1;j<M;j++){
if(palin[j]<=i)
dp[i][j]=(dp[i][j-1]+dp[i-palin[j]][j])%MOD;
else
dp[i][j]=dp[i][j-1];
}
}
while(t--)
solve();
return 0;
}
附带题目:P1025 [NOIP2001 提高组] 数的划分
题目描述
将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7n=7,k=3k=3,下面三种分法被认为是相同的。
1,1,5
1,5,1
5,1,1
问有多少种不同的分法。
输入格式
n
,
k
(
6
<
n
≤
200
,
2
≤
k
≤
6
)
n,k (6<n \le 200,2 \le k \le 6)
n,k(6<n≤200,2≤k≤6)
输出格式
11 个整数,即不同的分法。
输入
7 3
输出
4
四种分法为:
1,1,5
1,2,4
1,3,3
2,2,3
题解
这题其实是排列组合里的题,可以把一个数值为 n 的数当做 n 个小球,划分的份数 k 当做 k 个盒子,那么本题可以转化为“将 n 个小球放到 k 个盒子中,小球之间与盒子之间没有区别,并且最后的结果不允许空盒”
将 n 个小球放到 k 个盒子中的情况总数 =
a.至少有一个盒子只有一个小球的情况数 + b.没有一个盒子只有一个小球的情况数
这样进行划分是因为这种分类可以使a和b都有能写出来的表达式:
a.因为盒子不加区分,那么 1 的情况数与“将 n-1 个小球放到 k-1个盒子中”的情况数一样
b.没有一个盒子只有一个小球,那么把每个盒子中拿出来一个小球,对应的是“把 (n-k) 个小球放到 k 个盒子中的情况数”
然后将上面的思路化为动态转移方程:
设 f[n][k]
代表将 n 个小球放到 k 个盒子中且没有空盒的情况,
那么f[n][k] = f[n-1][k-1] + f[n-k][k]
而当 k=1 时只有 1 种方法(小球全部放进1个盒子)
int f[1010][100];
int n, k;
void solve(){
cin >> n >> k;
for (int i = 1; i <= n; i++)
f[i][1] = 1; //当 k=1 时只有 1 种方法
for (int i = 1; i <= n; i++)
for (int j = 2; j <= k; j++) // 1的情况已经处理,从2开始,否则会访问0。
if (i >= j) //小细节:只有n>=k时才能有数分。
f[i][j] = f[i - 1][j - 1] + f[i - j][j];
printf("%d\n", f[n][k]);
//输出从n中分出k份的方案数
}
本题数据量小,爆搜+剪枝也可以:
l a s t last last:上次划分所用分数
c u r cur cur:划分的次数
s u m sum sum:目前所组合的值
需要保证的条件: i ≤ n − s u m k − c u r i\le \dfrac{ n - sum}{k-cur} i≤k−curn−sum
void dfs(int last,int sum,int cur){
if(cur==k){
if(sum==n) cnt++;
return;
}
for(int i=last;sum+i*(k-cur)<=n;i++)//剪枝
dfs(i,sum+i,cur+1);
}
int main(){
scanf("%d%d",&n,&k);
dfs(1,0,0);
printf("%d",cnt);
}
D. Lost Arithmetic Progression
题目大意:
很久以前,您想到了两个有限算术级数 A A A 和 B B B。然后您发现了另一个序列 C C C,其中包含 A A A 和 B B B 共有的所有元素。不难看出, C C C 也是有限算术级数。多年后,你忘记了 A A A 是什么,但记住了 B B B 和 C C C。出于某种原因,你下定决心要找到这个丢失的算术级数。在你开始这个永恒的搜索之前,你想知道有多少不同的有限算术级数存在,它们可能是你丢失的级数 A A A。
如果两个算术级数的第一项、共同差或项数不同,则它们被认为是不同的。
可能有无限多这样的级数,在这种情况下,您甚至不会尝试寻找它们!在所有这些情况下打印 -1。
即使它们的数量有限,答案也可能非常大。因此,您只对找到模 1 0 9 + 7 10^9+7 109+7 的答案感兴趣。
输入
输入的第一行包含一个整数
t
(
1
≤
t
≤
100
)
t (1≤t≤100)
t(1≤t≤100),表示测试用例的数量。
每个测试用例的第一行包含三个整数 b 、 q b、q b、q 和 y ( − 1 0 9 ≤ b ≤ 1 0 9 、 1 ≤ q ≤ 1 0 9 、 2 ≤ y ≤ 1 0 9 ) y(-10^9≤b≤10^9、1≤q≤10^9、2≤y≤10^9) y(−109≤b≤109、1≤q≤109、2≤y≤109),分别表示 B B B 的第一项、公差和项数。
每个测试用例的第二行包含三个整数 c 、 r c、r c、r 和 z ( − 1 0 9 ≤ c ≤ 1 0 9 、 1 ≤ r ≤ 1 0 9 、 2 ≤ z ≤ 1 0 9 ) z(-10^9≤c≤10^9、1≤r≤10^9、2≤z≤10^9) z(−109≤c≤109、1≤r≤109、2≤z≤109),分别表示 C C C 的第一项、公差和项数。
输出
对于每个测试用例,打印包含单个整数的单行。
如果有无限多的有限算术级数可能是您丢失的级数 A,请输出 -1。
否则,打印有限算术级数的数量,这可能是您丢失的级数 A 模 109+7。特别是,如果没有这样的有限算术级数,则打印 0。
测试样例:
**input**
8
-3 1 7
-1 2 4
-9 3 11
0 6 3
2 5 5
7 5 4
2 2 11
10 5 3
0 2 9
2 4 3
-11 4 12
1 12 2
-27 4 7
-17 8 2
-8400 420 1000000000
0 4620 10
**output**
0
10
-1
0
-1
21
0
273000
题解:
如果 C C C 的所有元素都不存在于 B B B 中,那么答案是 0 0 0。检查以下 4 4 4 条件就足以检查 C C C 的所有元素是否存在于 B B B 中 -
- B ≤ B\leq B≤ 的第一项 C C C 的第一项,即 b ≤ c b\leq c b≤c。
- B ≥ B\geq B≥ 的最后一项 C C C 的最后一项,即 b + ( y − 1 ) q ≥ c + ( z − 1 ) r b+(y-1)q\geq c+(z-1)r b+(y−1)q≥c+(z−1)r。
- C C C的公差必须能被 B B B的公差整除,即 r m o d q = 0 r\bmod q=0 rmodq=0。
- C C C 的第一项必须在 B B B 中,即 ( c − b ) m o d q = 0 (c-b)\bmod q=0 (c−b)modq=0。
现在假设满足以下条件。让我们用 [ a , d , n ] [a,d,n] [a,d,n] 表示具有第一项 a a a、共同差 d d d 和 n n n 项数的算术级数 (AP)。
如果 b > c − r b>c-r b>c−r 则有无限数量的级数可以是 A A A 像 [ c , r , z ] [c,r,z] [c,r,z], [ c − r , r , z + 1 ] [c-r,r,z+1] [c−r,r,z+1], [ c − 2 r , r , z + 2 ] [c-2r,r, z+2] [c−2r,r,z+2] 等等。类似地,如果 b + ( y − 1 ) q < c + z r b+(y-1)q<c+zr b+(y−1)q<c+zr,则有无限个级数可以是 A A A,例如 [ c , r , z ] [c,r,z] [c,r,z], [ c , r , z + 1 ] [c,r,z+1] [c,r,z+1], [ c , r , z + 2 ] [c,r,z+2] [c,r,z+2] 等等。
否则,有有限数量的级数可以是 A A A。让我们数一数。令 A A A 为 AP [ a , p , x ] [a,p,x] [a,p,x] 和 l = a + ( x − 1 ) p l=a+(x-1)p l=a+(x−1)p。可以看出,对于任何有效的 A A A, l c m ( p , q ) = r lcm(p,q)=r lcm(p,q)=r, ( c − a ) m o d p = 0 (c-a)\bmod p=0 (c−a)modp=0, a > c − r a>c-r a>c−r 和 l < c + r z l<c+rz l<c+rz。前两个条件是微不足道的。第三个条件是必要的,因为如果 a ≤ c − r a\leq c-r a≤c−r 那么 c − r c-r c−r 将始终出现在 A A A 和 B B B 中,这与 C C C 包含 A A A 和 B B B 共有的所有术语的事实相矛盾.同样,第四个条件也是必要的。
根据第一个条件, p p p 唯一可能取的值是 r r r 的因数,它可以在 O ( r ) \mathcal{O}(\sqrt{r}) O(r) 中枚举。 l c m lcm lcm 条件可以在 O ( log r ) \mathcal{O}(\log r) O(logr) 中检查。对于 p p p 的特定值,有满足条件 2 和 3 的 a a a 的 r p \frac{r}{p} pr 可能值和满足条件 2 和 3 的 r p \frac{r}{p} pr 可能值条件 2 和 4。因此,答案是 ∑ l c m ( p , q ) = r ( r p ) 2 \displaystyle\sum_{lcm(p,q)=r}\Big(\frac{r}{p}\Big)^2 lcm(p,q)=r∑(pr)2。
时间复杂度: O ( t r log r ) \mathcal{O}(t\,\sqrt{r}\,\log r) O(trlogr)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const int maxn = 1e6 + 5;
const ll MOD = 1000000007;
ll gcd(ll a, ll b){
return a % b == 0 ? b : gcd(b, a % b);
}
ll lcm(ll a, ll b){
ll g = gcd(a, b);
return (a * b) / g;
}
void solve(){
ll b, c, q, r, y, z;
cin >> b >> q >> y;
cin >> c >> r >> z;
ll e = b + q * (y - 1);
ll f = c + r * (z - 1);
if (c < b || c > e || f < b || f > e || r % q != 0 || (c - b) % q != 0)
cout << 0 << '\n';
else if (c - r < b || f + r > e)
cout << -1 << '\n';
else{
ll ans = 0;
for (ll p = 1; p * p <= r; p++){
if (r % p == 0){
if (lcm(p, q) == r){
ll a = ((r / p) * (r / p)) % MOD;
ans = (ans + a) % MOD;
}
if (p * p != r && lcm(r / p, q) == r){
ll a = (p * p) % MOD;
ans = (ans + a) % MOD;
}
}
}
cout << ans << '\n';
}
}
int main()
{
ios::sync_with_stdio(0);
int t;
cin >> t;
while (t--)
solve();
return 0;
}