A - Reverse
题意:给出一个数组,寻找一个子区间,使得其字典序最小。
解:努力把小的放到前面。找到第一个 a[i]!=i 的位置和 i 的位置,反转。
代码:

#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 1000005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int p[maxx]; signed main() { int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); int l=0,r=0; for(int i=1;i<=n;i++){ if(p[i]!=i){ l=i; break; } } if(l==0){ for(int i=1;i<=n;i++) printf("%d ",p[i]); printf("\n"); continue; } for(int i=1;i<=n;i++){ if(p[i]==l){ r=i; break; } } for(int i=1;i<l;i++) printf("%d ",p[i]); for(int i=r;i>=l;i--) printf("%d ",p[i]); for(int i=r+1;i<=n;i++) printf("%d ",p[i]); printf("\n"); } return 0; }
B - Odd Swap Sort
题意:给出一个数组,每次交换和为奇数的两个数,问能否使其有序。
解:这种回答yes/no的题一般找个判断条件。已知只有奇数加偶数等于奇数,也就是说每次只能换一奇一偶,如果有两个偶数组成逆序对,那么无解,奇数同理。反之必然有解,详情请见冒泡排序。
代码:

#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n; int p[maxx]; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); vector<int> n1,n2; for(int i=1;i<=n;i++){ if(p[i]%2==0) n2.push_back(p[i]); else n1.push_back(p[i]); } int flag=1; if(n1.size()>=2) { for (int i = 0; i < n1.size() - 1; i++) { if (n1[i] > n1[i + 1]) { flag = 0; break; } } } if(n2.size()>=2) { for (int i = 0; i < n2.size() - 1; i++) { if (n2[i] > n2[i + 1]) { flag = 0; break; } } } if(flag) printf("YES\n"); else printf("NO\n"); } return 0; }
C - Inversion Graph
题意:给出一个数组,每对逆序对之间连一条边,问最终有多少个连通块。
解:首先不能找出每个逆序对然后跑并查集。通过观察发现,对于新加入的一个数,如果前面有比它大的数,那么它们属于一个连通块,不用再看。但是这个范围很模糊,如果同时小于两个较大值,那么这两个连通块应该合并,但现在做不到这一点。考虑合并连通块,将两个数合成一个,肯定选较大的作为这个连通块代表。将这些数放进栈里,栈的大小就是连通块个数。
代码:

#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 100005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n; int p[maxx]; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); stack<int> s; for(int i=1;i<=n;i++){ if(s.empty()||p[i]>s.top()) s.push(p[i]); else{ int t=s.top(); while(!s.empty()&&s.top()>p[i]) s.pop(); s.push(t); } } printf("%d\n",s.size()); } return 0; } // a b c d e f // add g //if b<g<c //del c d e
D. Big Brush
题意:有一个n*m的网格每次可以涂2*2的小方块,现在给出涂完的结果,求涂的顺序。
解:先找出一个2*2的同色方块,以此为开始bfs,枚举包含它的所有方块组。已经找到的方块标记为0,表示可以当任意一种颜色使。注意开始找到的方块组要全部加入队列,避免不连通现象。稍微有点难写。
- 判四个方块颜色是否相等(包括标记为0的)可以用set;
代码:

#include <bits/stdc++.h> using namespace std; #define ll long long #define maxx 1005 #define maxn 1005 #define maxm 200005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long int n,m; int a[maxx][maxx]; struct node{ int i,j,col; }; vector<node> ans; queue<pair<int,int> > q; int xx[4]={0,1,0,1}; int yy[4]={0,0,1,1}; int check(int x,int y) { if (x < 1 || y < 1 || x >= n || y >= m) return 0; set<int> s; for (int i = 0; i < 4; i++) { if (a[x + xx[i]][y + yy[i]]) s.insert(a[x+xx[i]][y+yy[i]]); } if(s.size()!=1) return 0; for (int i = 0; i < 4; i++) { if (a[x + xx[i]][y + yy[i]]) q.push(make_pair(x + xx[i], y + yy[i])); } ans.push_back((node) {x, y, *s.begin()}); a[x][y] = a[x + 1][y] = a[x][y + 1] = a[x+1][y+1] = 0; return 1; } signed main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); } for(int i=1;i<n;i++){ for(int j=1;j<m;j++) check(i,j); } while(!q.empty()){ int x=q.front().first; int y=q.front().second; q.pop(); for(int i=-1;i<=0;i++){ for(int j=-1;j<=1;j++) check(x+i,y+j); } } int flag=1; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(a[i][j]!=0) flag=0; } if(flag){ reverse(ans.begin(),ans.end()); printf("%d\n",ans.size()); for(auto [x,y,z]:ans) printf("%d %d %d\n",x,y,z); }else printf("-1\n"); return 0; }
E. Colorful Operations
题意:相当粗暴的题意。一开始数组中所有数val为0,颜色为1。有三种操作:
1.将区间[l,r]颜色赋值为x;
2.将所有颜色为x的数val+=y;
3.查询第x数的val。
解:学习了珂朵莉树。先分析一下题目。2操作很容易想到开一个数组col,存每个颜色到目前为止加了多少值,最后查询时再用当前值加上col[now]。途中这个结点会变好几次颜色,所以每变一次颜色就要加一次值。还有一个问题就是col[now]表示到目前为止累积和,而now在里面的时间很可能没有呆满,要预先减掉当前累积的值,这样再加上的时候答案才是对的。用x表示之前颜色,y表示要变成的颜色。现在来结算:val[now]=val[now]+col[x]-col[y].
现在执行1操作。区间赋值,这没什么,普通的线段树就能做,但赋值的同时要更新当前val,而每个区间内的颜色不尽相同,这样下去要变成单点修改了,不行。解决方法之一是多加一个same标签,记录一个区间颜色是否相同。这个做法实际上类似于势能线段树,复杂度远比看起来低。
另一个解法就是珂朵莉树。现在要解决的是大区间内小区间的颜色不同,如果能把这些小区间一个一个列出来就好了,听起来又精确又方便,于是珂朵莉树出现了。在数据随机的情况下,线段树能做的珂朵莉树都能做,因为它很暴力。珂朵莉树有两个操作,一是split,以左端点或右端点为界,把一个区间分成两个,二是assign,把两个区间合成一个。当区间覆盖时,以左端点为界,裂开,中间抹平,右边再裂开,完事。实现用set,删除原区间再塞一个进去。有点像分块,但比分块操作少。
复杂度可以到达nloglogn,具体证明在这(虽然没看懂
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?