模拟赛总结合集
2024
2024.4.20
T1
对于图上费用问题,可以考虑重构图。
T2
有颜色限制时,可以考虑次短路。
2024.3.30
T2
简化题意,完全图匹配的本质是任意两点都可匹配。考虑排序后 dp,\(dp(i,j,0/1)\) 表示选到 \(i\),已经匹配 \(j\) 组,第 \(i\) 个选/不选时最小匹配。有 \(dp(i,j,0)=\min(dp(i-1,j,0),dp(i-1,j,1)),dp(i,j,1)=dp(i-1,j,0)+a_{i}-a_{i-1},ans=\min(dp(n,m,0),dp(n,m,1))\)。
2024.3.23
T2
打完部分分可以考虑推广。
比如树上重心可以推广到树上 \(k\) 长重链,加一个维度即可。
T3
大模拟尽量把题意弄懂再开打。(主要是出题人的锅)。
2024.3.2
T2
二分答案时,注意实现答案可能会改变。即每次判断二分的解是否可行时,需要重新考虑解。
T4
考虑简化答案。基环树中的环的答案可先行求出。然后加上枝条的影响。
2024.1.20
T4
题意
给定 \(0\le l,r\le 10^9\),求
正解
分每一位考虑,
数位 dp,搜出第 \(k\) 位为 \(0\) 的数量 \(x\),第 \(k\) 位为 \(1\) 的数量 \(y=(r-l+1)-x\),答案即为
#include <iostream> using namespace std; typedef long long ll; constexpr int mod=1e9+7,N=40; int mem[N][2],a[N]; #define now mem[i][small] int dfs(int i,int k,int small=false)// cnt 0 { if(~now)return now; if(!i)return now=1; now=0; if(i==k)now=dfs(i-1,k,small||a[i]);// only 0 else now=dfs(i-1,k,small||a[i])+(small||a[i]?dfs(i-1,k,small):0);// 0/1 return now; } int calc(int x,int k) { if(x==-1)return 0; int cnt=0; while(x)a[++cnt]=x&1,x>>=1; fill(*mem,*mem+(N<<1),-1); return dfs(cnt,k+1); } int main() { int T,l,r,ans; ll tmp; scanf("%d",&T); while(T--) { scanf("%d %d",&l,&r); ans=0; for(int k=0;(1<<k)<=r;k++) { tmp=calc(r,k)-calc(l-1,k); tmp=tmp*((r-l+1)-tmp)%mod; ans=(ans+(tmp<<k)%mod)%mod; } printf("%d\n",(ans<<1)%mod); } return 0; }
2024.1.13
T2
题意
\(m\) 个方格,其中 \(n\) 个蹦床,第 \(i\) 个可以向右跳 \(l_i\) 格。
从 \(1\) 号方格开始,求到 \(m\) 的方案数,\(\bmod{10^9+7}\)。
考场解法
树状数组解决。
正确写法:
constexpr int N=1145141,p=1000000007; int n,m; namespace bit// range add, point get { int t[N]; #define lb (x&(-x)) inline void add(int x,int y){for(;x<N;x+=lb)t[x]=((t[x]+y)%p+p)%p;}// 错误:t[x]=(t[x]+y+p)%p; inline void add(int l,int r,int y){add(l,y),add(r+1,-y);} inline int get(int x){int res=0;for(;x;x-=lb)res=((res+t[x])%p+p)%p;return res;}// 错误:res=(res+t[x]+p)%p; }// namespace bit
取模不规范,亲人两行泪。
正解
前缀和。当然树状数组也可以。
T4
题意
\(n\),权值 \(v_i\),颜色 \(c_i\),取任意多组 \([l_i,r_i],r_{i-1}<l_i<r_i\),最大化
正解
dp+优化,
#include <iostream> using namespace std; typedef long long ll; constexpr int N=1145141; constexpr ll INF=1e18; int n,k; ll c[N],v[N],dp[N],j[N]; int main() { freopen("poker.in","r",stdin); freopen("poker.out","w",stdout); scanf("%d %d",&n,&k); for(int i=1;i<=n;i++)scanf("%lld",c+i),j[c[i]]=-INF; for(int i=1;i<=n;i++)scanf("%lld",v+i),v[i]+=v[i-1]; for(int i=1;i<=n;i++) { // dp[i]=dp[i-1]; // for(int j=1;j<i;j++)if(c[j]==c[i]) // { // dp[i]=max(dp[i],dp[j-1]+(v[i]-v[j-1])); // } //! dp[i]=max(dp[i],(dp[j-1]-v[j-1])+v[i])); dp[i]=max(dp[i-1],j[c[i]]+v[i]); j[c[i]]=max(j[c[i]],dp[i-1]-v[i-1]); } printf("%lld",dp[n]); return 0; }
2023
2023.12.30
T1
题意
给定一棵树,节点带权 \(v\),初始为 \(0\)。
\(q\) 个多测,每次给定点集 \(S\),表示 \(i\in S,v_i\gets 1\)。
每次操作可子树 \(\operatorname{xor} 1\)(翻转)。求最少操作次数使树中所有点权为 \(0\)。
- \(1\sim 6\)(Force):\(n,q\le 10^3\)。
- \(7\sim 8\)(A):\(|S|=1\)。
- \(9\sim 11\)(B):\(S\) 内点不相邻。
- \(12\sim 14\)(C):链,\(f_i=i-1\)。
- \(15\sim 16\)(D):\(f_i=\operatorname{random}[1,i-1]\)。
考场解法
8:32 开打
如果 \(i \in S\),必然有操作奇数个在祖先。
同时若 \(i\) 操作后为 \(1\),必然要以 \(i\) 为根操作。
dfs,额外记录祖先操作次数 \(o\),如果 \(o+[u \in S]\) 为奇数,要翻转。
以上 \(O(nq)\)。可拿 \(1\sim 6\)。
8:42 开打 \(O(n^2)\)
9:09 打完,测完,\(30\text{ pts}\) 到手。
9:13 特 A,\(|S|=1\),显然需要反转以 \(u\) 为底的子树。
同时需要反转 \(u\) 的所有儿子的子树。
答案为
e[u].size()
\(+1\)。9:20 打完,测完,\(40\text{ pts}\) 到手。
特 B,无直接连接。
9:37 寄。
特 C,链。
9:49 B 又懂了。打!
10:16 B、C 都打完了,样例除 \(7\) 外全过。
xor:\(70\text{ pts}\)
Note:
B:无直接连接可直接拆成 \(|S|\) 个 A 性质,一个一个做,这样翻转完 \(u\) 的子树再翻转所有儿子的子树,可做到只翻转 \(u\)。
C:合并连通块,答案即为连通块(同属于 \(S\))数量 \(\times 2\),注意最后一个特判减 \(1\)。
分数
\(70\text{ pts}\)。
正解
其实很近了,将 C 中链扩展为树,对树中连通块缩点,统计缩点后 \(S\) 中(多个缩成一个只算一个点)节点度数。
T2
考场解法
- subtask 1:直接判,输出
int(a[1]>a[2])
。 - subtask 2:不会(其实应该是搜,没时间想)。
- subtask 3:
namespace t3 { bool check(){int x=abs(a[1]);for(int i=2;i<=n;i++)if(x!=abs(a[i]))return false;return true;} void solve() { int cnt=1; ll ans=0ll; for(int i=1;i<=n;i++) { if(a[i]<0) { ans+=ll(abs(cnt-i)); cnt+=2; } } printf("%lld",ans); } }
- subtask 4:
namespace t4 { bool check(){int x=n>>1;for(int i=1;i<=x;i++)if(a[i]>0||(-a[i])!=a[i+x])return false;return true;} void solve(){n>>=1;printf("%lld",(1ll*(n-1)*n)>>1ll);} }
- subtask 5、6:不会。
分数
\(45\text{ pts}\)。
T3、T4
未看题、过难。
2023.12.23
T3
题意
长度为 \(n\) 的序列 \(a\),合并果子,代价为两堆重量的积,求所有情况总代价之和。
考场解法&正解
容易发现顺序不重要。
共
种,相乘即可。
代码:
#include <iostream> using namespace std; typedef long long ll; constexpr int N=114514,mod=998244353; ll n,a[N]; int main() { scanf("%lld",&n); ll pre=0,times=0,k=1; for(int i=1;i<=n;i++) { scanf("%lld",a+i); (times+=(a[i]*pre))%=mod; (pre+=a[i])%=mod; } for(ll i=n;i>1ll;i--) { (k*=(i*(i-1)>>1ll))%=mod; } // for(int i=2;i<=n;i++)(k*=i)%=mod; // (k=k*k/n)%=mod; printf("%lld",(times*k)%mod); return 0; }
2023.12.16
T1
题意
前缀 \(\operatorname{mex}\)。
考场解法&正解
#include <iostream> #include <bitset> using namespace std; constexpr int N=114514,A=1e9+7; int n,ans; bitset<A> cnt; int main() { freopen("mex.in","r",stdin); freopen("mex.out","w",stdout); scanf("%d",&n); for(int i=1,a;i<=n;i++) { scanf("%d",&a); cnt[a]=true; while(cnt[ans])ans++; printf("%d ",ans); } return 0; }
T2
题意
给三个集合,各选一个数,求最小的绝对值差(\(|a-b|+|a-c|+|b-c|\))。
考场解法
枚举 \(2\) 个,二分 \(1\) 个。
正解
枚举 \(1\) 个,二分 \(2\) 个。
T3
题意
钉针有一匹小马叫珍珠。为了赢下不久以后举行的小镇策马比赛,钉针在家里定制了两个长度为 \(n\) 米的跑道,让雪豹和珍珠在上面赛跑。
跑道是一个长度为 \(n\) 的字符串,每个字符只可能是 \(\texttt{E}\)、\(\texttt{W}\) 、\(\texttt{S}\) 、\(\texttt{N}\) 之一,分别表示(从当前的位置向)东、西、南、北延长 \(1\) 米。保证相邻的两个字符不会一个是 \(\texttt{S}\) 一个是 \(\texttt{N}\),也不会一个是 \(\texttt{E}\) 一个是 \(\texttt{W}\)。
珍珠和雪豹会同时站在跑道的起点,钉针则对他们同时下达指令:选择东、西、南、北之一的方向,让珍珠和雪豹同时向这个方向走 \(1\) 米。如果珍珠或雪豹当前的位置不存在这个方向的跑道,它就会在原地不动。如果有,即使它已经到达了终点,也会倒回去 \(1\) 米。
钉针想知道,在这种条件下,珍珠和雪豹能不能都到达终点。
正解
如果倒回去,必然存在 \(s\) 后缀为 \(t\) 后缀的取反。
暴力判即可。
T4
题意
也就是说,你有一个 \(n\) 个点,\(m\) 条边的无向简单连通图,点权初始全部为 \(0\)。接下来有 \(q\) 次操作或询问,分别是以下三种中的一种:
- \(1~p~v\),表示对 \(p\) 点和与 \(p\) 点直接相连的所有点进行点权 \(+v\) 的操作。
- \(2~p~v\),表示对 \(p\) 点和与 \(p\) 点直接相连的所有点进行点权 \(\times v\) 的操作。
- \(3~p\),你需要回答此时 \(p\) 点点权对 \(998244353\) 取模的值。
考场解法
暴力 \(1e3\)+bfs 序树转序列线段树维护 \(4e5\)。
线段树调用参数炸了,\(20\text{ pts}\)(暴力分)。
赛后调试日志:
20:03 开订
long long
20:12 订完了,交
Killed: Segmentation fault
md
20:19 开大,特判越界,交
20:27 试试 bfs
咦~ 还没 WA,先 RE 了
20:29 再试试
一样的
鉴定为 bfs
20:30 开大 bfs 相关数组
理论上不会 RE 的啊
20:32 康康 inputs
input 时 RE???
弱智
并不
到 bfs 都没问题
wssb| 调试用 &&0 忘关
忘关就是开啦md
编译一下发现忘打 = temp line 140)
弱智
20:44 交!
File Error fxxk
20:45 试试 modify
CE FXXK
20:47 交,RE,取之,modify
查 modify 罢
20:48 逝世 build
WA
看来是 modify 的问题
20:58 对之前的 40 pts,交
21:04 判
终于……
判松点试试
忽然懂了
有些节点是叶子,没有孩子,导致修改 [l,r]=[beg[u],end[u]]=[0,0]。
然后永远修不到,导致 RE。
放宽所有越界判断
21:12 交
60 pts
说明全是线段树黑箱外部原因。放宽外部判断到底线。
交
60 pts
缩紧seg数组
60 pts
与seg无关
看来它是好人
Part II 分块优化
牛逼!!!
只能说榜1dalao太秀了
21:29 开订暴力
21:43 订完
21:49 忘了样例里有 *
交
正解
分块 \(1e4\)+bfs 序树转序列线段树维护 \(4e5\)。
放上代码。
2023.12.9
T1
题意
给定长度为 \(n\) 的数列 \(a\),求对于每个 \(i\) 对应的 \(j\),\(ans_i=\max\{j\}-i-1\),\(ans_i<0\) 时输出 \(ans_i\gets-1\)。其中 \(j\) 满足 \(a_j<a_i\)。
考场解法&正解
排序后显然 \(a_i\) 左侧的 \(a_j\) 都满足 \(a_j<a_i\)。于是求前缀的 下标最大值 即可。
错误:选择以 \(a_i\) 升序排序。
没有考虑 \(a_i=a_j\) 的情况。此时 \(j\) 不合法。故要加一个关键字 \(id\) 升序。
当 \(a_i=a_j\) 时,若 \(i<j\),\(a_i\) 排前。这样就可以过滤 \(a_i=a_j\)。
分数
\(75\text{ pts}\)。
T2
题意
简化后:给出 \(s_{i,j}\in \{\texttt{a}\cdots\texttt{z}\}\)(\(i\le n\le 500,j\le m\le 500\)),要求改成:
- 每行只有两种不同字符。
- 相邻字符不同。
求改的次数(代价)的最小值。
正解
要求即:
- 每行交错两个,设为 \(a_i,b_i\)。
- 上下两行 \(a_i\ne a_{i-1},b_i\ne b_{i-1}\)。
明显状压:\(dp(i,a,b)\) 改到第 \(i\) 行(包含),改为 \(a_i=a,b_i=b\) 的最小代价。
分数
\(10\text{ pts}\)。
T3
题意
给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作。
每次操作的内容为:给定 \(x,y\),序列中所有 \(x\) 会变成 \(y\)。
代码:
int ans = 2147483647; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { if (a[i] == a[j]) ans = std::min(ans, j - i); } } std::cout << ans << std::endl;
请在每次修改后输出代码运行的结果。
考场解法
离散化+ set
。
正解
暴力,由于修改后 \(a\) 中不同颜色数量单调不增,所以答案为 \(1\) 后一直输出 \(1\) 即可。
以上解法玄学复杂度。忘记是从哪里看来的了。真·正解已丢失。
分数
\(100\text{ pts}\)。
T4
题意
洛谷 P4381 [IOI2008] Island。
即拆基环树,使直径最短。边带权。
考场解法
分数据:\(O(n^2)\)+特殊性质:环。
日常 long long
。
正解
紫题,拱出去!
疑似树形 dp。
分数
\(30\text{ pts}\)。
2023.12.2
T1
题意
给定一个数字,求写出它所需的笔画数。(\(4,5\) 算 \(2\) 画,其他 \(1\) 画。)
\(n\le 10^{101}\)。
考场解法&正解
显然输入字符串后遍历每位统计。
分数
\(100\text{ pts}\)。
T2
丢失。但 \(100\text{ pts}\)。
T3
题意
\(n\times m\)(\(n,m\le 500?\))的迷宫,给定走迷宫的策略:若前方是障碍,右转。
给定 \(q\) 个坐标,回答从此位置向 \(4\) 个方向分别能否走出迷宫。
考场解法
反着搜,从边界的位置反向搜出能走出迷宫所有状态(位置、方向)。
明显复杂了,也不好实现,打炸了。
没时间打暴力,输出 NNNN
骗分,一分没得。
分数
\(0\text{ pts}\)。
T4
题意
给定两个数组 \(a,b\),\(|a|=n,|b|=m\),\(n,m\le 5000?\)。对于每个 \(i\in[1,n]\),选择 \(a_i\) 或 \(b_i\),记 \(sum\) 为其和。
要求尽量连续选择:若上一种(选择 \(a_i\) 或 \(b_i\))连选了 \(k\) 个,这次切换后要至少连选 \(2k\) 个。
求 \(\min\{sum\}\)。
考场解法
\(dp(i,j,k)\)(\(i\in[1,n],j\in\{0,1\},k\in[,i]\))表示选完到第 \(i\) 个,最后选了 \(a_i/b_i\)(\(j=0/1\)),连续 \(k\) 个,最小 \(sum\)。
复杂度 \(O(n^3)\)。
正解
加上前缀最小值优化即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具