SMU 2026 Spring 天梯赛8题解
7-3 高温补贴
题意:
给定当日最高气温 \(T\)、工作场所状态 \(S\)(\(1\) 表示露天,\(0\) 表示室内)、工作场所温度 \(t\)。判断能否获得高温补贴并输出对应结果。
思路:
- 若 \(S=1\) 且 \(T > 35\) 且 \(t \ge 33\),则补贴,输出 Bu Tie 和 \(T\)。
- 否则若 \(T > 35\) 且 \(t \ge 33\) 且 \(S=0\),则因室内工作不补贴,输出 Shi Nei 和 \(T\)。
- 否则若 \(S=1\) 且 (\(T\le 35\) 或 \(t<33\)),则因不热不补贴,输出 Bu Re 和 \(t\)。
- 否则(\(S=0\) 且 (\(T\le 35\) 或 \(t<33\))),输出 Shu Shi 和 \(t\)。
代码
点击查看代码
void solve() {
int T, S, t;
cin >> T >> S >> t;
if (S == 1 && T > 35 && t >= 33) {
cout << "Bu Tie" << endl << T << endl;
} else if (T > 35 && t >= 33 && S == 0) {
cout << "Shi Nei" << endl << T << endl;
} else if (S == 1 && (T <= 35 || t < 33)) {
cout << "Bu Re" << endl << t << endl;
} else {
cout << "Shu Shi" << endl << t << endl;
}
}
7-4 零头就抹了吧
题意:
给定一个正整数 N,找到小于等于 N 的最大的“整”数。这里的“整”数定义为二进制表示中只有最高位是1,其余位都是0的数,即2的幂。
思路:
直接找到 N 的二进制最高位的位置 k,然后输出 2^k。因为 2^k 是小于等于 N 的最大的2的幂。
代码
点击查看代码
void solve() {
int n;
cin >> n;
int k = 31 - __builtin_clz(n);
cout << (1 << k) << endl;
}
7-5 这不是字符串题
题意:
给定一个整数序列,需要执行 M 次操作,操作有三种类型:
- 查找-替换:查找给定的连续子序列,如果找到第一个匹配的,则替换为另一个序列。
- 插入平均数:对整个序列,如果相邻两数之和为偶数,则在这两个数之间插入它们的平均数。
- 翻转:翻转指定区间内的子序列。
思路:
直接模拟所有操作,使用 vector 存储序列。
- 对于查找操作,直接枚举定位第一个匹配位置。
- 对于插入操作,每次记录前一个值和当前值,然后和为偶数的插入平均值。
- 对于翻转操作,直接翻转即可。
代码
点击查看代码
void solve() {
cin>>n>>m;
vector<int>q,q1;
for(int i=1;i<=n;i++){
int x;
cin>>x;
q.push_back(x);
}
while(m--){
int x;
cin>>x;
if(x==1){
int l,l1;
cin>>l;
vector<int>qq,q2;
for(int i=0;i<l;i++){
int x;
cin>>x;
qq.push_back(x);
}
cin>>l1;
for(int i=0;i<l1;i++){
int x;
cin>>x;
q2.push_back(x);
}
int ff=1;
for(int i=0;i+l-1<q.size();i++){
int f=1;
for(int j=0;j<l;j++){
if(q[i+j]!=qq[j]){
f=0;
break;
}
}
if(f){
ff=0;
for(int j=0;j<i;j++){
q1.push_back(q[j]);
}
for(int j=0;j<l1;j++){
q1.push_back(q2[j]);
}
for(int j=i+l;j<q.size();j++){
q1.push_back(q[j]);
}
break;
}
}
if(ff){
swap(q,q1);
}
}else if(x==2){
int ls=-1;
for(int i=0;i<q.size();i++){
if(i){
if((ls+q[i])%2==0){
q1.push_back((ls+q[i])/2);
}
}
q1.push_back(q[i]);
ls=q[i];
}
}else{
int l,r;
cin>>l>>r;
l--,r--;
for(int j=0;j<l;j++){
q1.push_back(q[j]);
}
for(int j=r;j>=l;j--){
q1.push_back(q[j]);
}
for(int j=r+1;j<q.size();j++){
q1.push_back(q[j]);
}
}
swap(q,q1);
q1.clear();
}
int f=1;
for(auto x:q){
if(f){
f=0;
}else{
cout<<' ';
}
cout<<x;
}
}
7-6 这是字符串题
题意:
统计字符串中每个小写字母出现的次数,并根据给定的每个字母的分数计算字符串的总分。
思路:
直接遍历字符串统计每个字母的出现次数,然后根据给定的分数计算总分。
代码
点击查看代码
int n,m,k;
int a[N],b[N];
string s;
void solve() {
cin>>s;
for(int i=0;i<26;i++){
cin>>a[i];
}
for(int i=0;i<s.length();i++){
b[s[i]-'a']++;
}
int ans=0;
for(int i=0;i<26;i++){
ans+=a[i]*b[i];
cout<<b[i]<<" \n"[i==25];
}
cout<<ans<<endl;
}
7-7 大幂数
题意:
给定正整数n,判断是否存在正整数k和m,使得n = 1^k + 2^k + ... + m^k。若存在多个,输出k最大的表达式。
思路:
从大到小枚举幂次k(从30到1),对于每个k,枚举m使得和等于n,若和大于n则跳过,枚举下一个k。找到第一个满足条件的k即为答案。
代码
点击查看代码
int qpow(int x, int n) {
int ans = 1;
while (n) {
if (n & 1) {
ans *= x;
if(ans>(1LL<<31)){
ans=(1LL<<31);
}
}
x *= x;
if(x>(1LL<<31)){
x=(1LL<<31);
}
n>>=1;
}
return ans;
}
int n,m,k;
int a[N],b[N];
void solve() {
cin>>n;
for(int i=30;i>=1;i--){
int s=0;
int r;
for(int j=1;j;j++){
s+=qpow(j,i);
r=j;
if(s>=n)break;
}
if(s==n){
for(int j=1;j<=r;j++){
if(j>1){
cout<<'+';
}
cout<<j<<"^"<<i;
}cout<<endl;
return;
}
}
cout<<"Impossible for "<<n<<"."<<endl;
}
7-8 现代战争
题意:
给定一个 \(n \times m\) 的矩阵,每次选择当前矩阵中最大元素,删除其所在行和列,剩余部分合并为 \((n-1) \times (m-1)\) 的矩阵。重复 \(k\) 次后输出最终矩阵。
思路:
将所有元素按值降序排序,依次处理每个元素。若当前元素所在行或列未被删除,则将其行和列标记为删除,并计入一次轰炸。重复直到完成 \(k\) 次轰炸。最后输出未被删除的行和列对应的元素。
代码
点击查看代码
int n,m,k;
int a[N],b[N];
int s[1005][1005];
void solve() {
cin>>n>>m>>k;
priority_queue<PIII,vector<PIII>,less<PIII>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int x;
cin>>x;
s[i][j]=x;
q.push({x,{i,j}});
}
}
while(k--){
while(a[q.top().second.first]||b[q.top().second.second]){
q.pop();
}
auto [x,u]=q.top();
q.pop();
auto [i,j]=u;
a[i]=b[j]=1;
}
for(int i=1;i<=n;i++){
if(a[i])continue;
int f=1;
for(int j=1;j<=m;j++){
if(b[j])continue;
if(f){
f=0;
}else{
cout<<' ';
}
cout<<s[i][j];
}cout<<endl;
}
}
7-9 算式拆解
题意:
给定一个带括号的四则运算表达式,表达式保证每个运算符及其对应的两个操作数都被一对圆括号括住。要求按运算执行顺序输出每一步的计算操作。输出时,如果操作数是上一步的计算结果,则不输出该操作数。
思路:
由于每个运算都被括号包裹,表达式结构类似二叉树。可以解析表达式构建二叉树,然后进行后序遍历(先左子树,再右子树,最后根节点)。后序遍历的顺序就是计算顺序。输出时,如果操作数是数字则输出数字,如果是子表达式的结果则不输出。
代码
点击查看代码
int n, m, k;
int a[N], b[N];
string s;
void f(int l, int r) {
if (l > r)return;
// cout << l << ' ' << r << endl;
int L=l,R=r;
if(s[l]=='('){
int ss=0;
for(int i=l;i<=r;i++){
if(s[i]=='(')ss++;
else if(s[i]==')'){
ss--;
if(ss==0){
f(l+1,i-1);
L=i+1;
break;
}
}
}
}
if(s[r]==')'){
int ss=0;
for(int i=r;i>=l;i--){
if(s[i]==')')ss++;
else if(s[i]=='('){
ss--;
if(ss==0){
f(i+1,r-1);
R=i-1;
break;
}
}
}
}
if(L==l&&R==r){
for(int i=l;i<=r;i++){
cout<<s[i];
}cout<<endl;
}else{
f(L,R);
}
}
void solve() {
cin >> s;
f(1, s.length() - 2);
}
7-10 三点共线
题意:
给定平面上 n 个点的坐标(x,y),其中 y 坐标只能是 0、1 或 2,是否存在三个不同的点位于一条非水平的直线上?
本题就请你找出所有共线的解。
思路:
枚举两个y去找另一个y有没有对应解,注意去重。
代码
点击查看代码
int n, m, k;
int a[3][N], b[N];
string s;
void solve() {
cin>>n;
vector<int>q[3];
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
a[y][x+1000000]=1;
q[y].push_back(x+1e6);
}
sort(q[0].begin(),q[0].end());
q[0].erase(unique(q[0].begin(),q[0].end()),q[0].end());
sort(q[1].begin(),q[1].end());
q[1].erase(unique(q[1].begin(),q[1].end()),q[1].end());
int f=1;
vector<PII>ans;
for(auto x:q[0]){
for(auto y:q[2]){
if((x+y)%2==0&&a[1][(x+y)/2]){
f=0;
ans.push_back({(x+y)/2,x});
}
}
}
sort(ans.begin(),ans.end());
ans.erase(unique(ans.begin(),ans.end()),ans.end());
for(auto [y,x]:ans){
cout<<"["<<x-1000000<<", 0] ["<<y-1000000<<", 1] ["<<y*2-x-1000000<<", 2]"<<endl;
}
if(f)cout<<-1<<endl;
}
7-11 胖达的山头
题意:
给定 n 个熊猫的活跃时间段(闭区间,时间格式为 hh:mm:ss),任何时间点每个山头最多只能有一只活跃熊猫。求最少需要多少个山头。
思路:
这是一个典型的区间重叠最大数问题。将每个时间点转化为秒数,使用差分数组统计每个时刻的活跃熊猫数,最大重叠数即为所需的最少山头数。
注意处理闭区间:在开始时间点+1,在结束时间的下一秒-1。时间范围是00:00:00到23:59:59(0-86399秒),需要86400个位置。
代码
点击查看代码
int n, m, k;
int a[N];
void solve() {
cin>>n;
for(int i=1;i<=n;i++){
int x,y,z;
char c;
int s;
cin>>x>>c>>y>>c>>z;
s=x*3600+y*60+z;
a[s]++;
cin>>x>>c>>y>>c>>z;
s=x*3600+y*60+z;
a[s+1]--;
}
int mx=0;
for(int i=0;i<86400;i++){
if(i)a[i]+=a[i-1];
mx=max(mx,a[i]);
}
cout<<mx<<endl;
}
7-12 被n整除的n位数
题意:
给定 n(1 < n ≤ 15)和区间 [a, b](1 ≤ a ≤ b < 10^15),找出所有 n 位数,满足从左到右前 i 位(1 ≤ i ≤ n)能被 i 整除。按递增顺序输出区间内的这些数,若无则输出 No Solution。
思路:
通过深度优先搜索(DFS)从最高位开始逐位构造数字,检查当前构造的前 i 位是否能被 i 整除。构造完成后检查是否在给定区间内。最后输出即可。
代码
点击查看代码
int n, l, r;
int fl=1;
void f(int x,int s){
// cout<<x<<' '<<s<<endl;
if(s==n){
if(x>=l&&x<=r){
cout<<x<<endl;
fl=0;
}
return;
}
x*=10;
for(int i=0;i<=9;i++){
if(s==0&&i==0)continue;
if((x+i)%(s+1)==0){
f(x+i,s+1);
}
}
}
void solve() {
cin>>n>>l>>r;
f(0,0);
if(fl){
cout<<"No Solution\n";
}
}
7-13 人生就像一场旅行
题意
给定n个城市和m条双向道路,每条道路有旅费和心情指数。从某个出发城市开始,走最便宜的路线(最短路径)到达其他城市,要求总旅费不超过预算b。输出所有可达城市,以及这些城市中沿途心情指数总和最高的城市。
思路
使用Floyd算法同时计算任意两城市间的最短路径长度和最大心情指数。对于每个查询,找出所有最短路径不超过预算的城市,然后从中选择心情指数最大的城市。
代码
点击查看代码
int n, b, m,k;
int mn[N][N];
int mx[N][N];
void solve() {
cin>>b>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
mn[i][j]=0;
}else{
mn[i][j]=1e18;
}
mx[i][j]=0;
}
}
for(int i=1;i<=m;i++){
int x,y,w,z;
cin>>x>>y>>w>>z;
mn[x][y]=w;
mn[y][x]=w;
mx[x][y]=z;
mx[y][x]=z;
}
for(int l=1;l<=n;l++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mn[i][l]+mn[l][j]<mn[i][j]){
mn[i][j]=mn[i][l]+mn[l][j];
mx[i][j]=mx[i][l]+mx[l][j];
}else if(mn[i][l]+mn[l][j]==mn[i][j]&&mx[i][j]<mx[i][l]+mx[l][j]){
mx[i][j]=mx[i][l]+mx[l][j];
}
}
}
}
while(k--){
int x;
cin>>x;
// cout<<x<<':'<<endl;
int f=1;
for(int i=1;i<=n;i++){
if(i==x)continue;
// cout<<i<<' '<<mn[x][i]<<endl;
if(mn[x][i]<=b){
if(f){
f=0;
}else{
cout<<' ';
}
cout<<i;
}
}
if(f){
cout<<"T_T\n";
continue;
}
cout<<endl;
vector<int>q;
int mmx=0;
for(int i=1;i<=n;i++){
if(i==x)continue;
if(mn[x][i]<=b&&mx[x][i]>mmx){
mmx=mx[x][i];
q.clear();
q.push_back(i);
}else if(mn[x][i]<=b&&mx[x][i]==mmx){
q.push_back(i);
}
}
f=1;
for(auto u:q){
if(f)
{
f=0;
}else{
cout<<' ';
}
cout<<u;
}cout<<endl;
}
}
7-14 影响力
题意:
将国家排列在 \(n\) 行 \(m\) 列的矩阵中,每个国家有自身实力 \(S\)。国家 \(A\) 影响国家 \(B\) 的代价为 \(S_A \times \text{dist}(A,B)\),其中 \(\text{dist}(A,B) = \max(|i_A-i_B|, |j_A-j_B|)\)。要求计算每个国家影响全球所有国家的总代价(即 \(S_A\) 乘以 \(A\) 到所有其他国家的距离之和)。
思路1:
恶心的推导题。
不难发现每个国家的总代价只跟它的位置和它自身的值有关。
然后就是推导,推到O(1)就求完了。需要用到的数学知识都比较简单,就是分类讨论比较恶心。
思路2:
\(\max(|i_A-i_B|, |j_A-j_B|)\)可以用切比雪夫距离和曼哈顿距离转化掉。
设位置 \((i,j)\) 的实力为 \(a[i][j]\),需求 \(d[i][j] = \sum_{k,l} \max(|i-k|,|j-l|)\),答案即为 \(a[i][j] \times d[i][j]\)。
利用切比雪夫距离与曼哈顿距离的关系:令新坐标 \(u = i+j\), \(v = i-j\),则 \(\max(|i-k|,|j-l|) = \frac{|u-u'|+|v-v'|}{2}\)。因此 \(d[i][j] = \frac{1}{2}(\sum |u-u'| + \sum |v-v'|)\)。
问题转化为计算每个点的新坐标 \(u,v\) 到所有点 \(u\) 坐标的曼哈顿距离之和与 \(v\) 坐标的曼哈顿距离之和。对 \(u\) 和 \(v\) 分别排序并计算前缀和,对于每个点,二分查找其 \(u,v\) 在排序数组中的位置,利用前缀和快速计算 \(\sum |u-u'|\) 和 \(\sum |v-v'|\),相加除以 \(2\) 得到 \(d[i][j]\),再乘以实力输出。
代码1
点击查看代码
int s1(int n) {
return (n * (n + 1)) / 2;
}
int s2(int n) {
return n * (n + 1) * (2 * n + 1) / 6;
}
void solve() {
cin >> n >> m;
vector<vector<int>>a(n + 2, vector<int>(m + 2, 0));
vector<vector<int>>ans(n + 2, vector<int>(m + 2, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
// cout << i << '_' << j << ":\n";
int p = i;
int l = j - 1, r = m - j;
if (l > r)swap(l, r);
if (l <= i - 1) {
ans[i][j] += 2 * s2(l + 1) - 3 * s1(l + 1) + l + 1;
if (r <= i - 1) {
ans[i][j] += s2(r - l) + (3 * l+1) * s1(r - l) + (2 * l * l+l) * (r - l);
ans[i][j] += s1((i - 1) - r) * m + (m * r) * ((i - 1) - r);
} else {
ans[i][j] += s2((i-1) - l) + (3 * l+1) * s1((i-1) - l) + (2 * l * l+l) * ((i-1) - l);
}
} else {
ans[i][j] += 2 * s2(i) - 3 * s1(i) + i;
}
if (l <= n-i) {
ans[i][j] += 2 * s2(l + 1) - 3 * s1(l + 1) + l + 1;
if (r <= n-i) {
ans[i][j] += s2(r - l) + (3 * l+1) * s1(r - l) + (2 * l * l+l) * (r - l);
ans[i][j] += s1((n-i) - r) * m + (m * r) * ((n-i) - r);
} else {
ans[i][j] += s2((n-i) - l) + (3 * l+1) * s1((n-i) - l) + (2 * l * l+l) * ((n-i) - l);
}
} else {
ans[i][j] += 2 * s2(n-i+1) - 3 * s1(n-i+1) + n-i+1;
}
l = i - 1, r = n - i;
if (l > r)swap(l, r);
// cout<<l<<' '<<r<<endl;
if (l <= j - 1) {
ans[i][j] += 2 * s2(l + 1) - 5 * s1(l + 1) + (l + 1)*3;
// cout<<2 * s2(l + 1) - 5 * s1(l + 1) + (l + 1)*3<<endl;
if (r <= j - 1) {
ans[i][j] += s2(r - l) + (3 * l) * s1(r - l) + (2 * l * l) * (r - l);
ans[i][j] += s1((j - 1) - r) * n + (n * r) * ((j - 1) - r);
} else {
ans[i][j] += s2((j-1) - l) + (3 * l) * s1((j-1) - l) + (2 * l * l) * ((j-1) - l);
}
} else {
ans[i][j] += 2 * s2(j) - 5 * s1(j) + j*3;
}
if (l <= m-j) {
ans[i][j] += 2 * s2(l + 1) - 5 * s1(l + 1) + (l + 1)*3;
if (r <= m-j) {
ans[i][j] += s2(r - l) + (3 * l) * s1(r - l) + (2 * l * l) * (r - l);
ans[i][j] += s1((m-j) - r) * n + (n * r) * ((m-j) - r);
} else {
ans[i][j] += s2((m-j) - l) + (3 * l) * s1((m-j) - l) + (2 * l * l) * ((m-j) - l);
}
} else {
ans[i][j] += 2 * s2(m-j+1) - 5 * s1(m-j+1) + (m-j+1)*3;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << ans[i][j]*a[i][j] << " \n"[j == m];
}
}
}
代码2
点击查看代码
void solve() {
cin >> n >> m;
vector<vector<int>>a(n + 2, vector<int>(m + 2, 0));
vector<vector<int>>ans(n + 2, vector<int>(m + 2, 0));
vector<int>q1,q2,s1,s2;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
q1.push_back(i+j);
q2.push_back(i-j);
}
}
sort(q1.begin(),q1.end());
sort(q2.begin(),q2.end());
s1=q1,s2=q2;
for(int i=1;i<q1.size();i++){
s1[i]+=s1[i-1];
}
for(int i=1;i<q2.size();i++){
s2[i]+=s2[i-1];
}
int nn=q1.size();
int mm=q2.size();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int p1=lower_bound(q1.begin(),q1.end(),i+j)-q1.begin();
int p2=lower_bound(q2.begin(),q2.end(),i-j)-q2.begin();
int ss=((p1+1)*(i+j)-s1[p1]+(s1[nn-1]-s1[p1])-(nn-1-p1)*(i+j));
ss+=((p2+1)*(i-j)-s2[p2]+(s2[mm-1]-s2[p2])-(mm-1-p2)*(i-j));
ans[i][j]=a[i][j]*ss/2;
cout<<ans[i][j]<<" \n"[j==m];
}
}
}

浙公网安备 33010602011771号