A - 信箱
-
如果起点
s
在区间[a[1], a[n]]
之外,直接移动到区间的远端。 -
如果起点
s
在区间内,计算移动到区间左端点或右端点的最短距离,然后遍历整个区间。
#include<iostream> #include<cstring> using namespace std; int n,s; int a[105]; int main(){ int t; cin>>t; while(t--){ memset(a,0,sizeof(a)); cin>>n>>s; int x; for(int i=1;i<=n;i++){ cin>>a[i]; } if(s>=a[n]||s<=a[1]){ if(s>=a[n] )cout<<s-a[1]<<endl; else cout<<a[n]-s<<endl; continue; } int mi=min(s-a[1],a[n]-s); int ans=mi+a[n]-a[1]; cout<<ans<<endl; } }
B - 天空之上
简化判断:关键观察:若存在合法拆分,最短的b(长度为 1)最容易满足子串条件。因为单个字符只要在a+c
中出现过即可。因此,我们可以只检查b
为单个字符的情况:
-
让
a
取s[0]
(第一个字符),c
取s[2..n-1]
(从第三个字符到结尾),则b
为s[1]
(第二个字符)。此时只需检查s[1]
是否在a+c
(即s[0] + s[2..n-1]
)中出现。 -
若上述情况不满足,再让
a
取s[0..n-3]
,b
取s[n-2]
,c
取s[n-1]
,检查s[n-2]
是否在a+c
中出现。
结论:若上述两种情况中任意一种满足,则输出Yes
;否则输出No
。
#include<iostream> #include<cstring> using namespace std; int n,s; int a[105]; int main(){ int t; cin>>t; while(t--){ memset(a,0,sizeof(a)); cin>>n>>s; int x; //int mx=0,mi=9999; for(int i=1;i<=n;i++){ cin>>a[i]; } if(s>=a[n]||s<=a[1]){ if(s>=a[n] )cout<<s-a[1]<<endl; else cout<<a[n]-s<<endl; continue; } int mi=min(s-a[1],a[n]-s); int ans=mi+a[n]-a[1]; cout<<ans<<endl; } }
C - 我的那些花儿
要解决这个问题,我们需要找到执行一次特定操作后矩阵中可能的最小最大值。操作定义为:选择一行r
和一列c
,将该行和该列的所有单元格值减一。核心目标是通过合理选择r
和c
,使操作后矩阵的最大值尽可能小。
-
获取最大值及数量:遍历矩阵,找到最大值
mx
,并统计其出现的总次数cnt_mx
。 -
统计每行每列的最大值数量:用数组
r[i]
记录第i
行中mx
的数量,用数组c[j]
记录第j
列中mx
的数量。 -
检查是否存在覆盖所有最大值的行和列:对每个单元格
(i,j)
,计算 “第i
行的mx
数量 + 第j
列的mx
数量 - 重复计算的交点(若(i,j)
是mx
则减 1)”。若该值等于cnt_mx
,说明第i
行和第j
列能覆盖所有mx
,标记flag=1
。 -
输出结果:若
flag=1
(存在有效行和列),则结果为mx - 1
;否则为mx
。
#include<iostream> #include<vector> using namespace std; int main() { int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector<vector<int>> a(n, vector<int> (m)); int mx = 0, cnt_mx = 0; //记录最大值及其出现次数 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> a[i][j]; if (a[i][j] > mx) mx = a[i][j], cnt_mx = 1; else if (a[i][j] == mx) cnt_mx++; } } vector<int> r(n), c(m); for (int i = 0; i < n; i++) {// 存每行、每列中最大值的数量 for (int j = 0; j < m; j++) { if (a[i][j] == mx) { r[i]++; c[j]++; } } } //检查是否存在一个位置,能覆盖所有mx int flag = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { //行i的mx数量+列j的mx数量-当前位置是否是mx==总mx数量 if (r[i] + c[j] - (a[i][j] == mx) == cnt_mx) { flag = 1; break; } } if(flag) break; } cout << mx - flag << endl; } return 0; }
E - 你有什么问题?
要解决这个问题,我们需要找到在区间[l, r]
内的整数x
,使得f(l, x) + f(x, r)
的值最小,其中f(a, b)
定义为两个数的十进制表示中对应位置数字相同的位数。
-
函数f(a, b)的性质:
f(a, b)
是a
和b
对应位数字相同的数量。例如f(123, 153) = 2
(百位和个位相同)。因此,f(l, x) + f(x, r)
的含义是:x
与l
相同的位数,加上x
与r
相同的位数,我们需要最小化这个总和。 -
关键观察:
-
若
l
和r
的最长公共前缀为p
(即前p
位数字完全相同,第p
位不同),则x
的前p
位可以与l
、r
保持一致(这样f(l, x)
和f(x, r)
在前p
位各贡献p
,总和为2p
)。 -
第
p
位是l
和r
的差异点,这一位的处理决定了总和的最小值。
-
-
分情况讨论:
情况 1:l == r此时
x
只能是l
(或r
),f(l, x)
和f(x, r)
均等于字符串长度(所有位都相同),因此总和为2 * 长度
。情况 2:l != r先计算最长公共前缀长度
p
(前p
位相同,第p
位不同)。-
若
l[p] + 1 < r[p]
(即第p
位存在中间数字,例如l[p] = '3'
,r[p] = '5'
,中间有'4'
):此时x
的第p
位可以取中间值(如'4'
),使得x
的第p
位与l
、r
均不同(f(l, x)
和f(x, r)
在第p
位贡献0
)。后面的位可以任意取值(只要x
在[l, r]
内),且与l
、r
均不同,因此总和为2p
(仅前p
位贡献)。 -
若
l[p] + 1 >= r[p]
(即第p
位相差1
,无中间数字,例如l[p] = '3'
,r[p] = '4'
):此时x
的第p
位只能是l[p]
或r[p]
(否则x
不在[l, r]
内)。无论取哪一个,第p
位的总和贡献为1
(例如x[p] = l[p]
时,f(l, x)
得1
,f(x, r)
得0
,总和1
)。因此基础总和为2p + 1
。此外,若后面的位i
满足l[i] = '9'
且r[i] = '0'
(因进位导致,例如l = "199"
,r = "200"
),x
的第i
位只能是9
或0
,每一位贡献1
,因此总和需加1
,直到遇到不满足该条件的位为止。
-
#include<iostream>//电子的问题 #include<string> using namespace std; string l, r; int main() { int t; cin >> t; while (t--) { cin >> l >> r; if (l == r) { cout << 2 * l.size() << endl; continue; } int p = 0; while (p < l.size() && l[p] == r[p]) p++; //最长公共前缀的长度p if (l[p] + 1 < r[p]) cout << 2 * p << endl; //存在中间数字 else { //无中间数字,相差 1 int rs = 2 * p + 1; for (int i = p + 1; i < l.size(); i++) { if (l[i] == '9' && r[i] == '0') rs++; else break; } cout << rs << endl; } } }