考试心得7
比较诡异的把暑假模拟赛劈成两半写。
以及把字体改成正常大小了。(原因是正常大小才有侧边栏的目录)
半屏状态下觉得侧边栏目录丑的点击右上角「目录」就可以关闭。
2026/8/10 | 2026CSP-S模拟赛9
感觉又几乎是打暴力的一场。但T1我真的努力了,虽然没什么用。
T1写完朴素dp式子后,发现这个可以线段树优化!!!然后就直接不顾一切的写完了,复杂度也是刚好可以过的。但大样例跑了6s多。但是我怎么算复杂度都是没有问题的,急急急。最后代码扔一边不管了。T2T3T4也是只会基础暴力(这个暴力还得带一点点脑子写点优化)嗯,依旧发呆很久。
然后考完突然发现,T1是多测!带了个T!过不了不稀奇了。看题解,转移时增加的答案至多有10种,而每一种只需取最靠右的决策点是最优的。预处理每种增加量的决策点。所以O(10nk)就做完了。啊还是相当简单啊。
T2没什么思路呀。题解是线性基+bitset。这个我记得以前模拟赛考过一次线性基,当时还学了来着,虽然现在忘光了。
就是考虑每个数至多有1个>1000的质因子,剩下的质因子都在1000以内,有168个,考虑用bitset存一下1000以内的。至于>1000的质因子,拎出来一个含该质因子的数作为新的基底,剩下的异或这个数后扔进线性基里。再将其余<1000的质因子的数的bitset扔进线性基里,求出有多少个能被线性基中的数表示出来,记为k,答案即为2^K-1。
至于为什么这样是对的。发现题目最终求的是质因子异或和为0的方案数。显然同一质因子出现偶数次即可。如果一个数能被其他数表示出来,那么异或起来刚好为0,因此那k个数可选可不选。「至于>1000的质因子,拎出来一个含该质因子的数作为新的基底」:解释一下,记这个>1000的质因子为x,那么含x的所有数除去一个留在外面,其余的扔进线性基里,若含x的选了奇数个,就把留在外面的那个补给他们,否则外面的那个就不用管,这样刚好使异或和为0。以及2^K-1也可以这样理解。
2026/8/11 | 2026CSP-S模拟赛7
bxT2送的40pts。
2026/8/12 | 2026CSP-S模拟赛11
纯打暴力来的。感觉罚坐了一半时间。
T1推完dp后就没什么思路了。赛时有想过分治(感觉处理很多区间的答案很像cdq分治),但是因为自己的思维能力和代码能力的局限而quit了。然后正解就是分治TT,而且很好写。考虑如何处理跨mid的区间[l,r]的答案。考虑从mid向两边推dp,拆成左右两部分的状态。设dp[i][0/1][0/1]表示在位置i,mid/mid+1 选/没选,i 选/没选的答案。设g[i][0/1]=max(dp[i][0/1][0],dp[i][0/1][1]),这样子合并状态,对于每个[l,r],答案就是max(g[l][0]+max(g[r][1],g[r][0]),g[l][1]+g[r][0])。然后考虑分离变量,将l和r各移到一侧,若g[l][1]+g[r][0]>g[l][0]+max(g[r][1],g[r][0]),即g[r][0]-max(g[r][1],g[r][0])>g[l][0]-g[l][1],就选g[l][1]+g[r][0]。这样就可以对每个r处理出g[r][0]-max(g[r][1],g[r][0])全部进行排序,然后对每个l lower_bound找到r的那个位置,前后缀和优化就能算出答案。
T2也是非常的有价值啊,首先来说一下部分分。35pts的状压dp:设dp[i][j][s]表示数字i放到j点上,放过点的状态为s的方案数,枚举i-1是放在哪个点上的,然后直接转移即可。我感觉我平常设状态会把j放到第一维,也就是先枚举点。此题给我开拓出新的思路了。然后就是链的容斥:如果树为一条链的话,就相当于求排列的数量,满足相邻数之间没有(a[i],a[i]+1)这种情况。考虑容斥,全集-至少一对不合法+至少两对不合法-至少三对不合法……(n-1)对不合法就是方案数。算的时候,从n-1对数中挑i对,把每一对看成一个数,乘上阶乘种排列方式。
然后就是喜闻乐见的正解了。考虑从链的情况推广到整棵树。其实树由多条同一方向的链组成,原来链中的相邻数对其实就是树上的边,那就考虑枚举有k条边不合法的情况,会发现剩余n-k个点可以随意排列。
然后现在就只需求出整棵树选k条不合法边的方案数了。因为有分叉,而同一个点只能同时接受一个入度和一个出度,所以它不同于普通的链。然后这个就很像树上背包的写法,设dp[u][x][k][0/1/2/3]表示u的子树内,前x个子树,选了k条不合法边,u节点无出度无入度/有1入度无出度/有1出度无入度/有1入度1出度的方案数。转移时枚举当前子树 前面的子树选的不合法边的大小i 和 该子树内选的不合法边的大小j,在当前这条边,像01背包一样分成选和不选两种情况。不选的话0/1/2/3都可以从前一个子树的0+1+2+3(省略前几维了)转过来(注意这里的j不能是0,然后选的话根据边的方向,和儿子的状态拼接即可。然后dp数组可以滚掉一维x,因此i和j要倒序枚举。然后就做完了。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5005, mod=998244353;
int n, mp[maxn][maxn], ans, vis[maxn], a[maxn], dp[maxn][maxn][4], siz[maxn];
int fac[maxn];
struct edge{
int nxt, to, w;
}edge[maxn<<1];
int head[maxn], edgenum;
void add(int from,int to,int w)
{
edge[++edgenum].nxt=head[from];
edge[edgenum].to=to;
edge[edgenum].w=w;
head[from]=edgenum;
}
void dfs(int u,int fa)
{
dp[u][0][0]=1;
for(int k=head[u];k;k=edge[k].nxt)
{
int v=edge[k].to;
if(v==fa) continue;
dfs(v, u);
for(int i=siz[u];i>=0;i--)//滚掉一维后倒序枚举
{
for(int j=siz[v];j>=0;j--)
{
//不选这条边
if(j)
{
(dp[u][i+j][0]+=dp[u][i][0]*(dp[v][j][0]+dp[v][j][1]+dp[v][j][2]+dp[v][j][3])%mod)%=mod;
(dp[u][i+j][1]+=dp[u][i][1]*(dp[v][j][0]+dp[v][j][1]+dp[v][j][2]+dp[v][j][3])%mod)%=mod;
(dp[u][i+j][2]+=dp[u][i][2]*(dp[v][j][0]+dp[v][j][1]+dp[v][j][2]+dp[v][j][3])%mod)%=mod;
(dp[u][i+j][3]+=dp[u][i][3]*(dp[v][j][0]+dp[v][j][1]+dp[v][j][2]+dp[v][j][3])%mod)%=mod;
}
//选
if(edge[k].w)
{
(dp[u][i+j+1][2]+=dp[u][i][0]*(dp[v][j][0]+dp[v][j][2])%mod)%=mod;
(dp[u][i+j+1][3]+=dp[u][i][1]*(dp[v][j][0]+dp[v][j][2])%mod)%=mod;
}
else
{
(dp[u][i+j+1][1]+=dp[u][i][0]*(dp[v][j][0]+dp[v][j][1])%mod)%=mod;
(dp[u][i+j+1][3]+=dp[u][i][2]*(dp[v][j][0]+dp[v][j][1])%mod)%=mod;
}
}
}
siz[u]+=siz[v];
}
siz[u]++;
// for(int i=0;i<siz[u];i++)
// {
// cout<<u<<" "<<i<<" "<<dp[u][i][0]<<" "<<dp[u][i][1]<<" "<<dp[u][i][2]<<" "<<dp[u][i][3]<<endl;
// }
}//
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int u, v;
cin>>u>>v;
add(u, v, 1);
add(v, u, 0);
}
dfs(1, 0);
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
for(int i=0;i<n;i++)
{
int t=1;
if(i%2) t=-1;
ans=(ans+t*fac[n-i]%mod*(dp[1][i][0]+dp[1][i][1]+dp[1][i][2]+dp[1][i][3])%mod+mod)%mod;
}
cout<<ans;
return 0;
}//
T3是博弈论555。由于正解非常的巧妙神奇因此就不写了(关键是我觉得有点困难了)然后就略写一下50分暴力吧。
巴什博弈 Nim游戏&sg定理或者这个
以防下次学的时候找不到博客了。
所以我们把每一堆石子一步定输赢,转化为0/1表示先手败/胜,变成常见的Nim游戏,这样子所有数异或起来就完了。
下次遇到这种类型的一定要试着会一下。别懵逼的一点暴力打不出来。
2026/8/14 | 2026暑期CSP-S模拟赛10
挂了点分。其实是送的分没拿到。这场暴力分特别多,人均分数都较高。
T1是大胆猜结论题。赛时我干瞪眼了将近一个小时,打了60分的暴力试图找规律,对于每个a把它可能的x都数出来,然后你就会惊人的发现这是有规律的(手玩一下就能推出来),

然后为了能把0~p-1的x都表示出来,就考虑最小的a满足着前面所有的x能覆盖完所需的点。此时我很明显能看出来a是不超过sprt(n),然后O(n sqrt(n))就可以做完了。
然而正解会发现答案不超过31,跑得相当快。我也荣获最劣解:)
T2记得以前学过floyed求最小环,然后就可以顺便求出最小环个数了。但最终只有55pts。原因和改到80pts是一样的。最短路可能有很多情况。
正解吧,从任意一个点出发,跑bfs,然后遇到访问过的点说明就成环了,就是因为没有边权,因此可以直接bfs求。然后跑完环之后累计个数,除以环长,若环长为奇数还要再除个2。然后我觉得写起来还挺麻烦的,尤其是判断两个环相接的那部分。
听完讲题的发现还是有更简单的做法,就是考虑对于每一条边,找到包含这条边的最小环。就是先删去这条边(u,v)(删完之后就不用加回来了),然后求出u到v的最短路径及其个数,每次更新即可。写起来非常方便。复杂度为O(n(n+m))。
T3也是相当()的一道题。考试时看到bi=1的,知道有nlogn的写法,但实在是忘了于是没写出来(这个dp还是没有掌握)。改完题后发现几乎是裸的LIS,设dp[i][j]表示前i个数,选出j的长度的最后一个数的最小值。这样子就会发现dp[i][j]随j的增加而增加,是有单调性的。然后对于当前的a[i],能转移的条件是a[i]>dp[i-1][j-1]*b[j-1],有效更新满足a[i]<dp[i-1][j],然后就能发现只有一个j满足这样的条件,就是第一个大于等于a[i]的dp[i-1][j],省去第一维。会发现和优化的LIS几乎一模一样。
2026/8/15 | 2026CSP-S模拟赛11
比较难绷。
T1就不说了。T2先打的暴力,(一开始写得还很丑,没有把一些冗余的东西写到一起),把暴力打完后,以为是树剖+线段树的神秘困难状物,遂放弃了,其实再回头看一眼式子就能发现分离后这是可以前缀和优化的啊啊啊,再加一个类似换根dp的东西,考完没怎么看题解就改完了。明明场上是可以做出来的。唉。
T3大家打的都是30,我当时还思考了很久矩阵怎么构造,然后想出来了f[n],f[n/2],f[n/4]……这种样子的矩阵,但是不知道怎么转移,而且优化了个寂寞。于是开了T4,发现是网络流的板子就立马开始写。但由于好久没写了 直接忘掉网络流建图的反向边不是双向边(网络流是有向图!有向图!!有向图!!!智障错误犯了inf次了)所以建成单向边了,加上题意有问题惨获0pts。然后就没有然后了。
T2的教训就是,在暴力分数很高的情况下,根据整理后的式子,一定要去多想想正解,昨天T2也是,打完70pts就直接跑路了。 切掉T1后T2就是至关重要的了。 有能力就去冲。
T3吧,还是很有意义的一道题。这个和前几天的T4很像,都是根据lowbit来进行性质的挖掘和优化。
总结貌似是写不完了。将来估计也没时间补。先放下题解吧。https://www.luogu.com.cn/article/vwoccsip
以及T3的教训是,(1<<i)这种东西在数值很大的情况下要写(1ll<<i)!!!这种小错误很难找出来而且会TLE、RE这些一起报出来,开警告也没用。非常感谢Zctf1088大佬帮我找出这个错误!!!
2026/8/17 | 2026CSP-S模拟赛14
切不了黄题。:)
T1,我不知道为什么我第一眼想到的是最长下降子序列,然后我在这个思路上努力了很久但过不了大样例,然后很不舍的放弃了 去打dp,结果忘了外面有层T于是写了n3。(考完发现自己dp状态定义的甚至是对的但就是没写成n2的,枚举了一层没用的)。总的来说感觉思路还是没有从「两个序列最后一个数」出发,没有抓住最核心的点。正解是很简单的贪心,记录两个序列末尾的数即可。但是我似乎没有很看懂题解的证明。
T2其实也是简单题。折半搜索带只log有80pts。优化的话就加个双指针就行了。我感觉我一直对双指针有点陌生,当时也没有想到这样优化。有点可惜了。以及双指针复杂度是O(n)的!因为朝一个方向移动至多有n次。
T3也是不难的计数题。
2026/8/18 | UKE Round 1
终于开始打大佬们出的模拟赛了。这场暴力很满,几乎没有罚坐时间。给部分分点赞。
草,九点半才开始写总结。
T3的势能分析线段树不应该想不到,做过类似的。
势能线段树
势能线段树(又称吉司机线段树)是一种针对无法用传统 lazy 标记高效处理的区间修改问题的优化线段树结构。它的核心思想是利用“势能”剪枝,避免在无效区间继续递归,从而显著降低复杂度。
传统线段树在区间修改时依赖 lazy 标记,但对于如区间开根号、取模、按位与、欧拉函数变换等依赖叶子节点真实值的操作,lazy 标记无法直接合并。这时,如果每次都递归到叶子节点,复杂度会退化到 O(n),而势能线段树通过维护额外信息(如区间最大值、区间或值、质因子集合等)来判断是否可以提前终止递归。
核心优化思路
-
维护势能标记:记录当前区间是否还有可能被修改(如最大值 > 1 才能继续开根号)。
-
剪枝条件:若当前区间的势能为零(例如最大值 ≤ 阈值),直接 return,不再递归。
-
递归更新:仅对势能不为零的子区间进行暴力修改。
-
push_up 合并信息:更新父节点的 sum、max、bitset 等信息。
典型应用场景与实现要点
-
区间开根号:维护区间最大值,max ≤ 1 时剪枝。
-
区间取模:max < 模数时剪枝。
-
因子函数替换:预处理因子个数表,max ≤ 2 时剪枝。
-
按位与:维护区间或值,若 (orsum & x) == orsum 则剪枝。
-
欧拉函数和:结合 lazy 标记与 bitset 维护质因子信息,支持快速批量乘法更新。
T1主要还是猜结论,结合部分分的做法,卡上下界的trick,以及树状数组优化的套路。
T2感觉没有那么ad-hoc,认识到逆排序的本质是交换两维后就好做了。对于每个点,记录一个三元组(i,j,a[i][j]),然后处理出m次操作后这三位位置上分别是原来的第几维,以及偏移量。
然后还改了T4。T4改起来似乎没这么难。就是会发现这个操作的本质就是形成一棵点分树,只不过不是重心当各个根,而是任意选的。就是要求这个方案数。考虑在原树的每一条边(u,v)上计算。因为一条边相当于连接了两个联通块(也就是两个点分树),求出合并的方案数就是划分的方案数。现在已经处理好u,v内部的划分情况,设这两棵点分树的根分别为x和y,那么对于这两棵点分树最先划分的就是x和y,然后一层一层按顺序来。那么只有x到u的路径和y到v的路径是实际上要考虑的。他们在各自点分树内的相对位置是不变的,就转化为有长为n、m的两个序列,合并后的方案数为 \(C_{n+m}^n\) 合并后共有n+m个数,从其中挑n个。设dp[u][i]表示u在它所处点分树内的深度为i,枚举u的子节点,将u和子节点全部合并起来。写起来就类似于一个树上背包,但滚掉一维后不能在原来的基础上累加,所以说不能倒序枚举来解决。
2026/8/19 | ZXCOI
改题很困难就是了。场上是一点都不会呀。讲题听不懂。
T1sb签到,没有hack的数据所以赛时不知道错哪了。当时的想法是,k为偶数时,k-2个数取到n,剩余两个数找到和最大的方案使它们异或起来为n(这个构造的方式就是推广到k个数后的方法),然后不知道错哪儿了。最后发现,k-2个n中,可以让某些数的最高位为0,然后在n为0的位上取到1,这样答案还可能更大。因此就从两个数推广到k个数,n为1的位上可以有k-1个1,积累尽量多的小于n的数,这样在n为0的位上就能有较多的贡献了。怎么说,我跟正解比就多了一根筋。罢了。
T2暴力打挂了。。每个测试的就错了一个数。改不动。温习了一下prufer序列。
T3觉得还是能改一些的。首先是扫描线,从左往右扫,所求的就是整个过程中被覆盖次数为km的个数。(1<=k<=n/m)。考虑用分块修改,查询,但每次查询需要n/m次,复杂度太大。
2026/8/21 | MKOI R1
出题风格很像平常打的模拟赛。以及感谢善良的出题人没有卡死T3也没有放多测。讲题很清楚!
T1很简单。也是这么久模拟赛中能自己切掉的了。
T2,赛时只会20暴力,其实精细实现会有30pts的。听懂了数位dp以前的东西。
T3还是可改的。赛时想到了求两个border,然后想到如果两个都求最长border,结果发现大样例全过了。自己造了组hack,但没发现是hack,原因竟是当成回文串了。痛失20pts。正解特别简单。考虑优化暴力,发现每次枚举一个i,求[i+1,n-i]的最长border至少是O(N)的。考虑每次利用已有的信息,也就是i从i+1转过来,能发现[i+1,n-i]比[i,n-i+1]的border至多少2个。那么就可以维护一个指针,来算出当前的border。因为指针最多回退2n次,那么总复杂度就是O(n)的。
2026/8/22 | 47OI R1
47的构造计数专场了(除了T4)。对不上脑电波。也是荣获暑假来最低的一场了。( ̄︶ ̄)
没赶上47讲题有点可惜。
T1真的想了好久啊。中途数据出锅了好几次,但也没影响到我不会做:)就是这个题吧,猜占了一半。然后我赛时只想到n为偶数不合法、可以对称过去、找到第一个大于m的2次幂。其实还是应该从手模更多数据以及打表猜出来(关键是暴力范围太小也不好打)。就会发现,2次幂的数可以连着放,来满足n个点的要求。因此就考虑找到最少的个数满足围成一圈后是2的幂,不够的再多放几个2次幂的数。比如m=41,那么填的就是 23,9,7,1,1,7,9,23。然后不够的可以填1。如果m为偶数,最小的可能是2、4、8……然后就这样构造一组方案,判断合不合法就行。思路就是:找最少的个数进行构造。严谨证明不会。
T2是很神奇的题目,但我觉得我想破头都想不出来。
T4终于不是构造题,感觉改起来是这几场中比较容易的T4了。首先,有60pts的暴力都非常好打,但我在赛时sb脑抽挂掉了。然后考虑它就是一个并查集合并的问题,如果一条边(x,y)只找到所有与它相交的线段,那么至多合并n次即可。注意到同一并查集中的点可以随意连,那么:

设h[x]表示x上方的线段个数(不包括连向x的),那么相邻之间的h之差不超过1。
对于一条边(x,y),x<y,那么:
- h[x]>h[y],找到x右边第一个h[p]<h[x]的点p,则p<=y,那么x可以与p合并。
- h[x]<h[y],找到y左边第一个h[p]<h[y]的点p,则p>=x,那么y可以与p合并。
- h[x]=h[y],找到p,如果p<y,就正常合并,否则之直接合并x和y。
不断重复这个操作,直至x与y在一个联通块中。这样子用很少的次数找到相交的线段。这个查找可以用线段树二分来解决。线段树维护h的最小值即可。
合并后h也要进行修改。若两个线段不交,那么这两个线段之间h++;若包含,那么较短的需--。
然后我update中没写pushdown惨调2.5h。
“祈祷今晚烟花再点燃一遍”
#include <bits/stdc++.h>
#include <windows.h> // Windows 图形界面支持
using namespace std;
const int WIDTH = 80;
const int HEIGHT = 25;
// 获取控制台句柄
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 在指定位置打印字符
void gotoxy(int x, int y) {
COORD pos = {x, y};
SetConsoleCursorPosition(hConsole, pos);
}
// 设置颜色
void setColor(int color) {
SetConsoleTextAttribute(hConsole, color);
}
// 烟花粒子结构
struct Particle {
float x, y; // 位置
float vx, vy; // 速度
float life; // 生命值
float maxLife; // 最大生命
int color; // 颜色
char symbol; // 符号
};
// 烟花爆炸效果
class Firework {
private:
vector<Particle> particles;
bool alive = true;
int centerX, centerY;
int type; // 0: 圆形, 1: 心形, 2: 星形
public:
Firework(int x, int y, int t = 0) {
centerX = x;
centerY = y;
type = t;
int numParticles = 80 + rand() % 60;
int baseColor = rand() % 14 + 1;
// 金色、红色、蓝色、紫色、青色、粉色、白色
int colors[] = {6, 12, 9, 13, 11, 13, 15};
int mainColor = colors[rand() % 7];
for (int i = 0; i < numParticles; i++) {
Particle p;
p.x = x;
p.y = y;
// 根据类型决定初始速度方向
float angle;
if (type == 0) { // 圆形
angle = (rand() % 360) * 3.14159 / 180;
float speed = 0.5 + (rand() % 100) / 100.0 * 2.5;
p.vx = cos(angle) * speed;
p.vy = sin(angle) * speed;
} else if (type == 1) { // 心形
angle = (rand() % 360) * 3.14159 / 180;
float r = 0.8 + (rand() % 100) / 100.0 * 1.5;
p.vx = 16 * pow(sin(angle), 3) * r / 8;
p.vy = -(13 * cos(angle) - 5 * cos(2*angle) - 2 * cos(3*angle) - cos(4*angle)) * r / 8;
} else { // 星形
angle = (rand() % 360) * 3.14159 / 180;
float r = 0.5 + (rand() % 100) / 100.0 * 2.0;
float starAngle = 5 * angle;
float starR = cos(starAngle) * 0.5 + 0.5;
p.vx = cos(angle) * starR * 3;
p.vy = sin(angle) * starR * 3;
}
p.life = 0.8 + (rand() % 100) / 100.0 * 0.8;
p.maxLife = p.life;
p.color = mainColor + (rand() % 3 - 1);
if (p.color < 1) p.color = 1;
if (p.color > 15) p.color = 15;
p.symbol = (rand() % 3 == 0) ? '*' : (rand() % 2 ? '.' : '+');
particles.push_back(p);
}
}
bool update() {
if (!alive) return false;
bool anyAlive = false;
for (auto &p : particles) {
p.x += p.vx;
p.y += p.vy;
p.vy += 0.03; // 重力
p.vx *= 0.99; // 空气阻力
p.vy *= 0.99;
p.life -= 0.008;
if (p.life > 0) anyAlive = true;
}
if (!anyAlive) alive = false;
return alive;
}
void draw() {
for (auto &p : particles) {
if (p.life <= 0) continue;
int px = (int)p.x;
int py = (int)p.y;
if (px >= 0 && px < WIDTH && py >= 0 && py < HEIGHT) {
// 亮度随生命值变化
int brightness = (int)(p.life / p.maxLife * 100);
if (brightness > 50) {
setColor(p.color);
} else {
setColor(p.color - 1);
if (p.color < 1) p.color = 1;
}
gotoxy(px, py);
if (brightness > 70) {
cout << p.symbol;
} else if (brightness > 40) {
cout << "·";
} else {
cout << " ";
}
}
}
}
};
// 背景星星
vector<pair<int, int>> stars;
void initStars() {
for (int i = 0; i < 50; i++) {
stars.push_back({rand() % WIDTH, rand() % HEIGHT});
}
}
void drawStars() {
setColor(7);
for (auto &s : stars) {
gotoxy(s.first, s.second);
if (rand() % 3 == 0) cout << "·";
else cout << " ";
}
}
// 主函数
int main() {
srand(time(0));
// 隐藏光标
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(hConsole, &cursorInfo);
cursorInfo.bVisible = false;
SetConsoleCursorInfo(hConsole, &cursorInfo);
system("cls");
// 初始化背景星星
initStars();
vector<Firework> fireworks;
int frame = 0;
// 文字祝福
string messages[] = {
"== 愿你的代码如烟花般绚烂! ==",
"== 每一行代码,都是夜空中最亮的星 ==",
"== 愿Bug远离,AC常伴! ==",
"== 算法之路,繁花似锦! ==",
"== 今晚,你就是最耀眼的烟火! =="
};
while (true) {
// 每帧随机生成新烟花
if (frame % 3 == 0 && fireworks.size() < 15) {
int x = 5 + rand() % (WIDTH - 10);
int y = 3 + rand() % (HEIGHT - 8);
int type = rand() % 3;
fireworks.push_back(Firework(x, y, type));
}
// 绘制背景
drawStars();
// 更新和绘制烟花
for (auto it = fireworks.begin(); it != fireworks.end();) {
it->update();
it->draw();
if (!it->update()) {
it = fireworks.erase(it);
} else {
++it;
}
}
// 在底部显示祝福文字(滚动)
int msgIdx = (frame / 30) % 5;
gotoxy(12, HEIGHT);
setColor(14); // 金色
cout << messages[msgIdx];
for (int i = 0; i < 15; i++) cout << " ";
// 显示"按Q退出"
gotoxy(60, HEIGHT);
setColor(8);
cout << "[按 Q 退出]";
Sleep(60);
frame++;
// 检测退出
if (GetAsyncKeyState('Q') & 0x8000) break;
}
// 恢复光标
cursorInfo.bVisible = true;
SetConsoleCursorInfo(hConsole, &cursorInfo);
system("cls");
cout << "== 烟花落幕,但你的代码之旅永不落幕! ==\n";
return 0;
}

浙公网安备 33010602011771号