[题解]2026杭电DEBUG杯完整题解

2026 DEBUG 杯程序设计大赛题解

1001:友好数

难度:简单 ,出题人:潘俊龙

【思考过程】
题目是解方程 \(y-d(y)=x\)。第一反应可把 \(y\) 挪到右边得到 \(y=x+d(y)\),于是候选 \(y\) 一定在 \(x\) 附近,而不是全范围乱枚举。

【方法一:小区间枚举】
对本题数据,\(y\) 位数最多 10 位,数位和最多 90,所以 \(y\in[x,x+90]\)。实作时枚举 \([x,x+100]\) 更稳妥。
逐个计算 \(d(y)\),若 \(y-d(y)=x\) 则计数。
时间复杂度 \(O(100\times 10)\),可视为单组 \(O(1)\)。

【方法二:按数位和反推】
也可枚举可能的数位和 \(s\),令 \(y=x+s\),再检查 \(d(y)=s\)。
该法本质与方法一等价,但枚举对象从 \(y\) 变成 \(s\),实现上同样简单。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int getsum(int x){
    int s=0;
    while(x){
        s+=x%10;
        x/=10;
    }
    return s;
}
void solve(){
    int x;
    cin>>x;
    int ans=0;
    for(int y=x;y<=x+100;y++){
        if(y-getsum(y)==x) ans++;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;cin>>T;
    while(T--) solve();
    return 0;
}

1002:星域导航

难度:简单,出题人:林苏阳
题解
【思考过程】
在树中,任意两个节点之间的路径唯一。
如果一条路径上的所有节点颜色都相同,那么这两个节点一定处在同一个 同色连通块 中。
所谓同色连通块,就是:

  • 连通块内所有节点颜色相同;
  • 连通块内节点之间通过颜色相同的边连接。

对于一个大小为 s 的同色连通块,其中任意两个不同节点之间的路径都只会经过这个连通块中的节点,因此都满足条件。

所以这个连通块对答案的贡献为:

\[C_s^2 = \\frac{s(s - 1)}{2} \]

最终答案就是所有同色连通块贡献之和。

【方法一:并查集按边合并】
遍历每条边 \((u,v)\):若 \(c_u=c_v\) 就合并。
每个连通块大小为 \(s\),贡献 \(\binom{s}{2}=s(s-1)/2\),累加即答案。
时间复杂度 \(O(n\alpha(n))\),空间复杂度 \(O(n)\)。

【方法二:DFS/BFS 连通块统计】
只在“同色边”上建图,再 DFS/BFS 求每个连通块大小。
总复杂度 \(O(n)\),常数略大于并查集,但同样可过。

C++ Solution(并查集)

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=2e5+10;
int c[N];
int f[N];
int sz[N];
int find(int x){
    if(f[x]!=x){
        f[x]=find(f[x]);
    }return f[x];
}
void unionn(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy&&c[fx]==c[fy]){
        sz[fy]+=sz[fx];
        f[fx]=fy;
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        cin>>c[i];
        f[i]=i;
        sz[i]=1;
    }
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        unionn(u,v);
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(f[i]==i){
            ans+=sz[i]*(sz[i]-1)/2;
        }
    }cout<<ans<<endl;
}

1003:洗盘子

难度:中等,出题人:张景杰

【题目大意】
A可以用a分钟洗盘子+装菜,可以用c分钟用干净盘子装菜
B可以用b分钟洗盘子
求n分钟内,A最多装几盘菜
【考点】数学,思维
【性质分析】

  • B肯定要一直洗盘子
  • A要进行 \(a\) 分钟或者 \(c\) 分钟的决策
  • 分三种情况
    1. \(a \le c\),那么 A 肯定选 \(a\) 分钟的独自做就行了,答案是 \(\left\lfloor\frac{n}{a}\right\rfloor\)。
    1. \(b < c < a\),A 肯定优先用 \(c\) 分钟去装菜,而他最早能在第 b 分钟开始装菜,因为洗盘子所需要的时间小于状态,所以他可以一直装菜直到结束,能够装 \(\left\lfloor\frac{n-b}{c}\right\rfloor=num\) 盘,而空闲出来的时间可以用 a 分钟来状态,可以装 \(\left\lfloor\frac{n-num\cdot c}{a}\right\rfloor\) 盘,两者相加就是答案。
    1. \(b < a < c\),因为洗盘子比装菜快,所以最后一个洗出来的盘子是没时间拿来装菜的,那么能够装 \(\left\lfloor\frac{n-c}{b}\right\rfloor=num\) 盘,而空闲出来的时间可以用 a 分钟来状态,可以装 \(\left\lfloor\frac{n-num\cdot c}{a}\right\rfloor\),两者相加就是答案。
      【时间复杂度】
  • \(O(1)\)
    【代码实现(c++)】
#include<bits/stdc++.h>
using namespace std;
long long a,b,c;
void solve(){
    cin>>n>>a>>b>>c;
    long long num=min((n-c)/b,(n-b)/c);
    cout<<max(num+(n-num*c)/a,n/a);
    return ;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

1004:回转01串

难度:难 , 出题人:潘俊龙

【思考过程】
原操作“看两侧是否相等再翻中间”表面看像是在直接翻位,但更关键的是:一次操作只会影响与位置 i 相邻的“相邻关系”,并不会对整串产生任意变化。
因此先找不变量:首尾位永远不会改变;接着把难点从“翻转某一位”改写成“相邻关系怎么变”,这样才可能落到可计算模型上。

于是思路链是:

  1. 用差分 \(D_i=A_i\oplus A_{i+1}\) 记录相邻是否相同;
  2. 条件 \(A_{i-1}=A_{i+1}\) 等价于 \(D_{i-1}=D_i\),而翻转 \(A_i\) 会让这两段差分同时取反;
  3. 再做奇偶变换 \(C_i=D_i\oplus(i\bmod 2)\),把“同时取反”转化为在 \(C\) 上的相邻交换 01 <-> 10
  4. 相邻交换的最小次数就变成经典问题:1 的相对顺序不变,最优代价由匹配到目标中第 \(j\) 个 1 的位置差给出。

【方法一:差分 + 奇偶变换 + 相邻交换模型】

  1. 先定义差分串 \(D_i=A_i\oplus A_{i+1}\);
  2. 再定义 \(C_i=D_i\oplus(i\bmod 2)\);
  3. 可证明一次合法操作等价于在 \(C\) 上做一次相邻交换 01 <-> 10
    于是最少操作数转化为:把 \(C_A\) 变成 \(C_B\) 的最小相邻交换次数。
    若两串 1 个数不同则无解;否则答案是对应第 \(j\) 个 1 的位置差绝对值之和。
    时间复杂度 \(O(n)\)。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N=1e6+5;
int posa[N],posb[N];
void solve(){
    int n;cin>>n;
    string a,b;cin>>a>>b;
    if(a[0]!=b[0]||a[n-1]!=b[n-1]){
        cout<<-1<<endl;
        return;
    }
    int cnta=0,cntb=0;
    for(int i=1;i<n;i++){
        int da=(a[i-1]!=a[i]),db=(b[i-1]!=b[i]);
        int ca=da^(i&1),cb=db^(i&1);
        if(ca) posa[++cnta]=i;
        if(cb) posb[++cntb]=i;
    }
    if(cnta!=cntb){cout<<-1<<endl;return;}
    int ans=0;
    for(int i=1;i<=cnta;i++) ans+=abs(posa[i]-posb[i]);
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;cin>>t;
    while(t--) solve();
    return 0;
}

【方法二:最近合法点 + 左推贪心】
这道题除了把操作转成差分串上的相邻交换以外,还可以直接从原操作本身出发做贪心。

一次操作选择位置 i,要求两侧相等,即 A[i-1]=A[i+1],然后翻转 A[i]
也就是说,如果用一个位置 x=i+1 来表示“中心为 i 的操作是否合法”,那么合法条件就是:

\[A_{x}=A_{x-2} \]

我们从左到右考虑。首尾字符永远不会被操作改变,所以如果首尾不同,一定无解。
之后假设已经把 1..i-1 都修好了,那么后面无论怎么操作,都不能再改这些位置。因此当前第 i 位如果和目标不同,就必须让位置 i 被翻转一次。

关键在于:位置 i 能不能翻,取决于 i 这个中心是否合法,也就是 x=i+1 是否是合法点。
如果 i+1 本来不合法,但更右边有一个最近合法点 x,那么可以从右往左依次操作:

\[x-1,\\ x-2,\\ \\cdots,\\ i \]

因为中间所有合法点都不存在,所以每向左翻一次,都会把左边相邻的非法状态变成合法状态。
这一串操作的效果就是把区间 [i,x-1] 整体翻转一次,花费 x-i 次操作,并且刚好能把第 i 位修正。

所以每次遇到当前位置不匹配时,都应该找当前最靠左的合法点 x
如果找不到,就无法再翻转当前位置,答案为 -1;如果找到了,代价就是 x-i,并用差分懒标记记录区间 [i,x-1] 被整体翻转。

  1. 首先判断首尾是否相同,因为首尾不会被任何操作改变;
  2. 用集合 s 维护当前所有合法点 x,其中 2<=x<nA[x]=A[x-2]
  3. 从左到右扫描每一位 i
    • 用懒标记落实当前位置是否被之前的区间翻转影响;
    • 如果当前位已经等于目标位,直接跳过;
    • 否则必须翻转当前位,找到最小合法点 x>=i+1
    • 若不存在,则无解;
    • 否则执行“从 x-1 左推到 i”这一段操作,答案加 x-i,并记录区间 [i,x-1] 翻转。
  4. 一次区间翻转只会影响右边界附近新的合法性,因此只需要更新 x+1 这个合法点。

为什么贪心正确:
当扫描到第 i 位时,前面的位已经固定,后面不能再通过操作影响它。
如果 A[i]!=B[i],那么中心 i 必须被操作一次。要让中心 i 合法,只能从右侧某个已经合法的位置一路向左传递。设当前最近的合法点是 x,那么至少需要经过 x-1,x-2,...,i,代价至少是 x-i;而上述操作恰好能用 x-i 次完成,所以这是当前最优选择。每一步都固定一个位置,因此整体最优。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int INF=(1LL<<60);
void solve(){
    int n;cin>>n;
    string a,b;cin>>a>>b;
    if(a[0]!=b[0]||a[n-1]!=b[n-1]){
        cout<<-1<<endl;
        return;
    }
    set<int>s;
    s.insert(INF);
    for(int i=2;i<n;i++){
        if(a[i]==a[i-2]) s.insert(i);
    }
    vector<int>tag(n+2,0);
    int ans=0;
    for(int i=1;i<n;i++){
        while(*s.begin()<i+1) s.erase(s.begin());
        tag[i]^=tag[i-1];
        if(tag[i]) a[i]^=1;
        if(a[i]==b[i]) continue;
        int x=*s.begin();
        if(x==INF){
            cout<<-1<<endl;
            return;
        }
        ans+=x-i;
        tag[i]^=1;
        tag[x]^=1;
        s.erase(x);
        if(x+1<n){
            if(a[x-1]!=a[x+1]) s.insert(x+1);
            else s.erase(x+1);
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;cin>>T;
    while(T--) solve();
    return 0;
}

1005:被四整除

难度:中等 , 出题人:潘俊龙

【思考过程】
关键不是“会不会死一次”,而是“是否能进入循环”。只要某次触发回满,系统就回到初始状态,后续无限重复。

【方法一:有限步枚举】
首次回满前,第 \(t\) 次受击后血量为 \(hp_t=n-td\)。
问题等价于是否存在 \(t\ge 1\) 使:

\[n-td>0,\\quad (n-td)\\bmod m=0 \]

若存在这样的 \(t\),则回满后状态重回初始,之后必无限重复,输出 YES;否则最终降到 \(\le 0\),输出 NO

把第二个条件写成同余形式:

\[(n-td)\\equiv 0 \\pmod m \\quad\\Longleftrightarrow\\quad td\\equiv n \\pmod m \]

设 \(g=\gcd(d,m)\)。同余存在最小正解且不超过 \(m/g\)(因此不超过 \(m\)),所以只需枚举 \(t=1,\ldots,\min\left(m,\left\lfloor\frac{n-1}{d}\right\rfloor\right)\) 即可。

单组复杂度 \(O(m)\)(本题 \(m\le 100\))。
(题目数据中的大数据不小心全部造成有限步内一定能满足 hp % m == 0 的情况了,实际大部分赛时代码都会 TLE。)

【方法二:数论判定(线性同余 + 模逆)】
触发回满等价于存在 \(t\ge 1\) 满足:

\[td\\equiv n\\pmod m,\\qquad n-td>0 \]

线性同余定理:令 \(g=\gcd(d,m)\),同余有解当且仅当 \(g\mid n\);否则直接 NO
若 \(g\mid n\),令:

\[d'=\\frac d g,\\ n'=\\frac n g,\\ m'=\\frac m g \]

因 \(\gcd(d',m')=1\),可在模 \(m'\) 下求逆元 \(inv\equiv (d')^{-1}\pmod{m'}\),从而最小非负解满足:

\[r\\equiv n'\\cdot inv\\pmod{m'} \]

此时最小正解为 \(t_{\min}=\begin{cases}r,&r>0\\ m',&r=0\end{cases}\)。只需检查 \(t_{\min}\le \left\lfloor\frac{n-1}{d}\right\rfloor\) 即可输出 YES/NO(逆元用扩展欧几里得求)。

C++ Solution
方法一代码:有限步枚举

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
void solve(){
    int n,d,m;
    cin>>n>>d>>m;
    int lim=(n-1)/d;
    int mx=min(m,lim);
    for(int t=1;t<=mx;t++){
        int hp=n-t*d;
        if(hp>0&&hp%m==0){
            cout<<"YES"<<endl;
            return;
        }
    }
    cout<<"NO"<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T=1;
    cin>>T;
    while(T--) solve();
    return 0;
}

方法二代码:数论判定(线性同余 + 模逆)

#include <bits/stdc++.h>
using namespace std;
#define int long long
int extgcd(int a,int b,int &x,int &y){
    if(!b){x=1;y=0;return a;}
    long long x1,y1;
    long long g=extgcd(b,a%b,x1,y1);
    x=y1;
    y=x1-a/b*y1;
    return g;
}
int inv(int a,int mod){
    int x,y;
    extgcd(a,mod,x,y);
    x%=mod;
    if(x<0) x+=mod;
    return x;
}
void solve(){
    int n,d,m;cin>>n>>d>>m;
    int g=std::gcd(d,m);
    if(n%g){cout<<"NO\n";return;}
    int d1=d/g,m1=m/g,n1=n/g;
    int r=(__int128)n1*inv(d1%m1,m1)%m1; //r∈[0,m1-1]
    int tmin=r?r:m1 ,lim=(n-1)/d; 
    cout<<(tmin<=lim?"YES\n":"NO\n");
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;cin>>T;
    while(T--) solve();
    return 0;
}

1006:别让厕纸被用完了!

难度:中等 ,出题人:方凯新

【思考过程】
两次补纸之间可以看成一个长度为 k 的周期。由于每次补纸都会把纸盒补满到 m,所以只要保证每个周期内的总耗纸量不超过 m 即可。

关键在于:只需要检查第一个周期 [1,k]
i 个会员来的时间是 1, 1+aᵢ, 1+2aᵢ, ...,在任意连续 k 天中,他最多来 ceil(k/aᵢ) 次,而第一个周期 [1,k] 中正好就是这个次数。
因此第一个周期一定是最坏情况。

于是判断一个 k 是否可行,只需检查:
sum(bᵢ * ceil(k/aᵢ)) <= m

随着 k 变大,每个 ceil(k/aᵢ) 都不会变小,所以总耗纸量单调不下降,可以二分最大合法的 k

【方法:二分答案】
check(k) 表示周期长度为 k 时是否够用。
i 个会员在第一个周期 [1,k] 中来的次数为:
(k+aᵢ-1)/aᵢ
这里是整数除法。
如果所有会员的总消耗不超过 m,则 k 合法,否则不合法。
二分上界可以由单个会员限制得到。对于任意会员 i,必须满足:
bᵢ * ceil(k/aᵢ) <= m
所以有:
k <= aᵢ * floor(m/bᵢ)
对所有会员取最小值,作为二分右边界。
如果 k=1 都不合法,说明第 1 天所有人一起来时纸都不够,直接输出 0

时间复杂度 \(O(n \log V)\),其中 \(V\) 为二分上界。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
using i128 = __int128_t;

void solve(){
    int n,m;
    cin>>n>>m;
    vector<int>a(n+1),b(n+1);
    int r=LLONG_MAX;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
        r=min(r,a[i]*(m/b[i]));
    }
    auto check = [&](int k)->bool{
        i128 sum=0;
        for(int i=1;i<=n;i++){
            int cnt=(k+a[i]-1)/a[i];
            sum+=(i128)cnt*b[i];
            if(sum>m) return false;
        }
        return sum<=m;
    };
    if(!check(1)){
        cout<<0<<endl;
        return;
    }
    int l=1;
    while(l<r){
        int mid=l+(r-l+1)/2;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

1007:寻找隐藏暗号

难度:中等 ,出题人:林苏阳

【题目大意】
给定两个字符串 ST
需要统计 S 的所有非空子串中,有多少个子串包含 T 作为子序列。
注意:子串必须是连续的一段;子序列不要求连续,只要求字符相对顺序不变;即使两个子串内容相同,只要起止位置不同,也要分别计数。

【思路】
设:\(n = |S|,m = |T|\)。由于 |T| <= 50,可以考虑动态规划。
我们从左到右扫描字符串 S,维护当前已经匹配到 T 的哪些位置。
状态定义:
定义数组 \(dp\):表示当前扫描到某个位置时,有多少个子串已经匹配了 \(T\) 的前 \(i\) 个字符。

也就是说:
\(dp[0]\) 表示还没有匹配到 \(T\) 中任何字符的子串数量;
\(dp[1]\) 表示已经匹配了 \(T[0]\) 的子串数量;
\(dp[2]\) 表示已经匹配了 \(T[0..1]\) 的子串数量;
...
\(dp[m]\) 表示已经完整匹配了整个 \(T\) 的子串数量。

状态转移:
每扫描到 \(S\) 中的一个字符 \(s\),都可以新开一个以当前字符为结尾的子串,所以:\(dp[0]++;\)
然后枚举 \(T\)中的位置。
如果当前字符 s 可以匹配 T[i],那么原来已经匹配了前 i 个字符的子串,现在就可以进一步匹配到前 i + 1 个字符:
\(dp[i + 1] += dp[i];\)
\(dp[i] = 0;\)
答案统计:
每处理完一个字符后,\(dp[m]\) 表示当前已经完整匹配了 \(T\) 的子串数量。
这些子串都可以作为合法答案,因此:\(ans += dp[m];\)
最终输出 \(ans\)即可。

【复杂度分析】
设 \(n = |S|\),\(m = |T|\)。
每个字符最多枚举一次 \(T\),所以时间复杂度为:\(O(nm)\)
由于 \(m \le 50\),可以通过本题。
空间复杂度为:\(O(m)\)

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
void solve(){
    string S,t;cin>>S>>t;
    int m=t.size();
    vector<int>dp(m+1,0);
    int ans=0;
    for(char s:S){
        //新开一个以当前字符为起点的子串
        dp[0]++;
        //倒序枚举
        for(int i=m-1;i>=0;i--){
            if(t[i]==s){
                dp[i+1]+=dp[i];
                dp[i]=0;
            }
        }
        ans+=dp[m];
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int t=1;cin>>t;
    while(t--){solve();}
}

1008:论坛热度监控

难度:中等 出题人:潘俊龙

【思考过程】
三个操作里最难的是“区间内最左满足阈值位置”,它要求维护值域信息并支持快速向左优先搜索,直接指向线段树。

【方法一:懒标记线段树】
维护每个结点:

  • mx:区间最大值;
  • tag:区间加懒标记。
    操作实现:
  1. 区间加:标准 lazy add;
  2. 区间最大值:标准 query;
  3. 找最左位置:若当前结点 mx<k 直接剪枝,否则先递归左子树,再递归右子树。
    单次 \(O(\log n)\),总复杂度 \(O(m\log n)\)。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define INF 0x3f3f3f3f3f3f3f3f
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
const int N=2e5+5;
int n,m;
int a[N];
struct node{
    int mx,tag;
}tr[N<<2];
void pushup(int x){
    tr[x].mx=max(tr[x<<1].mx,tr[x<<1|1].mx);
}
void addtag(int x,int v){
    tr[x].mx+=v;
    tr[x].tag+=v;
}
void pushdown(int x){
    if(!tr[x].tag) return;
    addtag(x<<1,tr[x].tag);
    addtag(x<<1|1,tr[x].tag);
    tr[x].tag=0;
}
void build(int x,int l,int r){
    tr[x].tag=0;
    if(l==r){
        tr[x].mx=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
void modify(int x,int l,int r,int L,int R,int v){
    if(L<=l&&r<=R){
        addtag(x,v);
        return;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if(L<=mid) modify(x<<1,l,mid,L,R,v);
    if(R>mid) modify(x<<1|1,mid+1,r,L,R,v);
    pushup(x);
}
int querymx(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R) return tr[x].mx;
    pushdown(x);
    int mid=(l+r)>>1;
    int ans=-INF;
    if(L<=mid) ans=max(ans,querymx(x<<1,l,mid,L,R));
    if(R>mid) ans=max(ans,querymx(x<<1|1,mid+1,r,L,R));
    return ans;
}
int querypos(int x,int l,int r,int L,int R,int k){
    if(r<L||R<l) return -1;
    if(tr[x].mx<k) return -1;
    if(l==r) return l;
    pushdown(x);
    int mid=(l+r)>>1;
    int res=-1;
    if(L<=mid) res=querypos(x<<1,l,mid,L,R,k);
    if(res!=-1) return res;
    if(R>mid) res=querypos(x<<1|1,mid+1,r,L,R,k);
    return res;
}
void solve(){
    cin>>n>>m;
    rep(i,1,n) cin>>a[i];
    build(1,1,n);
    while(m--){
        int op; cin>>op;
        if(op==1){
            int l,r,x; cin>>l>>r>>x;
            modify(1,1,n,l,r,x);
        }
        else if(op==2){
            int l,r; cin>>l>>r;
            cout<<querymx(1,1,n,l,r)<<endl;
        }
        else{
            int l,r,k; cin>>l>>r>>k;
            cout<<querypos(1,1,n,l,r,k)<<endl;
        }
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

1009:旋转方阵

难度:中等 , 出题人:张景杰

【题目大意】旋转一个方阵,开始和每次旋转会让方阵中的数向下掉落,求反转k次后的方阵。

【思考过程】模拟矩阵旋转操作,
步骤1:先处理最开始的下落,
步骤2:让矩阵顺时针旋转,然后下落,重复k次。

【复杂度分析】每次下落和旋转都是 \(O(n^2)\),
要进行k次模拟下落和旋转,
综上时间复杂度为 \(O(n^2 k)\)。

C++ Solution

#include<bits/stdc++.h>
using namespace std;
int n,k;
int g[507][507];
int c[507][507];
int gc[507][507];
void shun(){//顺时针旋转矩阵
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            c[i][j]=g[n-j+1][i];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=c[i][j];
        }
    }
    return ;
}

void move(){//下落
    for(int i=1;i<=n;i++){
        int num=0;
        for(int j=n;j>=1;j--){
            if(g[i][j]==0)num++;
            else break;
        }
        for(int j=1;j<=n;j++){
            if(j<=num)c[i][j]=0;
            else c[i][j]=g[i][j-num];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=c[i][j];
        }
    }
    return ;
}
void firstmove(){//第一次下落
    for(int i=1;i<=n;i++){
        for(int j=n;j>=1;j--){
            if(g[i][j]!=0){
                for(int k=j+1;k<=n;k++){
                    if(g[i][k]==0)swap(g[i][k],g[i][k-1]);
                }
            }
        }
    }
    return ;
}
void print(){//打印答案
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(g[i][j]==0)cout<<". ";
            else cout<<g[i][j]<<" ";
        }cout<<"\n";
    }
    return ;
}
void solve()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            string s;
            cin>>s;
            if(s==".")g[i][j]=0;
            else g[i][j]=stoll(s);
            gc[i][j]=g[i][j];
        }
    }
    if(k==0){
        print();
        return ;
    }
    firstmove();
    shun();
    k--;
    if(k==0){
        print();
        return ;
    }
    while(k--){
        move();
        shun();
    }
    print();
    return ;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int QAQ=1;cin>>QAQ;
    while(QAQ--) solve();
    return 0;
}
posted @ 2026-05-01 14:55  艾拉别哭  阅读(57)  评论(0)    收藏  举报