2025-11-10 NOIP 模拟赛5 赛后总结
Record
- 08:02 思考半个小时之后会了 \(O(2^3 nV)\) 做法。
- 08:22 想到容斥做法。
- 09:33 哦我草居然过大洋里了。润 T2 去。
- 10:05 大力分讨,启动!
- 11:12 我草,过大洋里了
- 11:26 打了 T3 的巨大常数暴力。
- 11:48 打了 T4 的暴力。
结果是 100+100+45+15。
啥 T1 就我一个切出来???
啥我是 rank1???
T1 密码
题意
给定一个字符串。
原串中的大写字母和数字不能改变,小写字母可以选择变成其对应的大写字母,? 需要变成小写字母、大写字母、数字中的其中任意一个。
变化后的字符串必须出现至少一个大写字母、至少一个小写字母、至少一个数字。且任意相邻两个字符不能相同。
求有多少种合法的变化后的字符串。
空间限制 32MB。
赛时
想了状压 DP。但是发现好像做不了。然后就去想容斥。
然后经过两个小时的精细实现过了。
题解
对“至少一个大写字母”“至少一个小写字母”“至少一个数字”三个条件进行容斥。
考虑钦定变化后的串无限制、不包含大写字母、不包含小写字母、不包含数字、不包含大写字母和数字、不包含大写字母和小写字母、不包含小写字母和数字。
计算完之后就是一坨加加减减了。
答案是 无限制 - 不包含大写字母 - 不包含小写字母 - 不包含数字 + 不包含大写字母和数字 + 不包含大写字母和小写字母 + 不包含小写字母和数字 这么一坨式子。
考虑如何计算。
考虑 \(dp_{i,c}\) 表示第 \(i\) 个字符为 \(c\) 的时候,当前答案有多少种。则 \(dp_{i,c}\) 可以从 \(dp_{i-1,c'},c'\ne c\) 转移。
然后发现可以把第一维滚掉。
注意钦定的限制,不能使用特定类型的字符。
代码排版可能有些乱,但是一些需要说明的注释都写了。
262 行顶级史山。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;
const long long mod=998244353;
int getid(char c){
if(islower(c)) return c-'a';
if(isupper(c)) return c-'A'+26;
if(isdigit(c)) return c-'0'+52;
return 62;
} // a-z 0-25 ; A-Z 26-51 ; 0-9 52-61 ; ? 62 ;
int gettype(char c){
if(islower(c)) return 0;
if(isupper(c)) return 1;
if(isdigit(c)) return 2;
return 3;
} // a-z 0 ; A-Z 1 ; 0-9 2 ; ? 3 ;
int n;
char a[100010];
long long b[2][62];
long long calc_000(){ // ban none
if(gettype(a[1])==0){
b[1][getid(a[1])]=1;
b[1][getid(a[1])+26]=1;
}else if(gettype(a[1])==1) b[1][getid(a[1])]=1;
else if(gettype(a[1])==2) b[1][getid(a[1])]=1;
else{
for(int i=0;i<62;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=0;i<62;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
b[i&1][getid(a[i])+26]=(lastsum-b[~i&1][getid(a[i])+26]+mod)%mod;
}else if(gettype(a[i])==1) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else if(gettype(a[i])==2) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else{
for(int j=0;j<62;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=0;j<62;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_001(){ // ban a-z
if(gettype(a[1])==0){
b[1][getid(a[1])+26]=1;
}else if(gettype(a[1])==1) b[1][getid(a[1])]=1;
else if(gettype(a[1])==2) b[1][getid(a[1])]=1;
else{
for(int i=26;i<62;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=26;i<62;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])+26]=(lastsum-b[~i&1][getid(a[i])+26]+mod)%mod;
}else if(gettype(a[i])==1) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else if(gettype(a[i])==2) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else{
for(int j=26;j<62;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=26;j<62;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_010(){ // ban A-Z
if(gettype(a[1])==0){
b[1][getid(a[1])]=1;
}else if(gettype(a[1])==1) return 0;
else if(gettype(a[1])==2) b[1][getid(a[1])]=1;
else{
for(int i=0;i<26;i++) b[1][i]=1;
for(int i=52;i<62;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=0;i<26;i++) lastsum+=b[1][i];
for(int i=52;i<62;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
}else if(gettype(a[i])==1) return 0;
else if(gettype(a[i])==2) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else{
for(int j=0;j<26;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
for(int j=52;j<62;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=0;j<26;j++) (lastsum+=b[i&1][j]+mod)%=mod;
for(int j=52;j<62;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_100(){ // ban 0-9
if(gettype(a[1])==0){
b[1][getid(a[1])]=1;
b[1][getid(a[1])+26]=1;
}else if(gettype(a[1])==1) b[1][getid(a[1])]=1;
else if(gettype(a[1])==2) return 0;
else{
for(int i=0;i<52;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=0;i<52;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
b[i&1][getid(a[i])+26]=(lastsum-b[~i&1][getid(a[i])+26]+mod)%mod;
}else if(gettype(a[i])==1) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else if(gettype(a[i])==2) return 0;
else{
for(int j=0;j<52;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=0;j<52;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_011(){ // ban a-z A-Z
if(gettype(a[1])==0){
return 0;
}else if(gettype(a[1])==1) return 0;
else if(gettype(a[1])==2) b[1][getid(a[1])]=1;
else{
for(int i=52;i<62;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=52;i<62;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
return 0;
}else if(gettype(a[i])==1) return 0;
else if(gettype(a[i])==2) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else{
for(int j=52;j<62;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=52;j<62;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_101(){ // ban a-z 0-9
if(gettype(a[1])==0){
b[1][getid(a[1])+26]=1;
}else if(gettype(a[1])==1) b[1][getid(a[1])]=1;
else if(gettype(a[1])==2) return 0;
else{
for(int i=26;i<52;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=26;i<52;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])+26]=(lastsum-b[~i&1][getid(a[i])+26]+mod)%mod;
}else if(gettype(a[i])==1) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else if(gettype(a[i])==2) return 0;
else{
for(int j=26;j<52;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=26;j<52;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
long long calc_110(){ // ban A-Z 0-9
if(gettype(a[1])==0){
b[1][getid(a[1])]=1;
}else if(gettype(a[1])==1) b[1][getid(a[1])]=1;
else if(gettype(a[1])==2) return 0;
else{
for(int i=0;i<26;i++) b[1][i]=1;
}
long long lastsum=0;
for(int i=0;i<26;i++) lastsum+=b[1][i];
for(int i=2;i<=n;i++){
for(int j=0;j<62;j++) b[i&1][j]=0;
if(gettype(a[i])==0){
b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
}else if(gettype(a[i])==1) b[i&1][getid(a[i])]=(lastsum-b[~i&1][getid(a[i])]+mod)%mod;
else if(gettype(a[i])==2) return 0;
else{
for(int j=0;j<26;j++) b[i&1][j]=(lastsum-b[~i&1][j]+mod)%mod;
}
lastsum=0;
for(int j=0;j<26;j++) (lastsum+=b[i&1][j]+mod)%=mod;
}
memset(b,0,sizeof b);
return lastsum;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>(a+1);
long long ans1=calc_000();
long long ans2=((calc_001()+calc_010()+mod)%mod+calc_100())%mod;
long long ans3=((calc_110()+calc_101()+mod)%mod+calc_011())%mod;
cout<<((ans1-ans2+mod)%mod+ans3+mod)%mod<<"\n";
# ifndef ONLINE_JUDGE
cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
# endif
return 0;
}
T2 追逐游戏
题意
形式化太难了我直接放原题面自己看吧。
小 Z 和小 Y 正在一棵 \(n\) 个点的无根树上玩追逐游戏。
小 Z 会在 \(0\) 秒时从 \(S\) 点出发,以每秒一条边的速度前往 \(T\) 点,且小 Z 会沿着这两点之间的最短路移动。在小 Z 到达 \(T\) 点后,他会一直停在 \(T\) 点,直到他被小 Y 抓住。
小 Y 会在 \(0\) 秒时从 \(S'\) 点出发,且小 Y 完全了解小 Z 的行动路线。每秒小 Y 可以选择沿着与当前节点连接的一条边移动,或者停在当前节点不动。小 Y 会选择最优的方式移动,使他能够尽早抓住小 Z。我们称小 Y 在 \(t\) 秒时抓住了小 Z,当且仅当在 \(t\) 秒时,小 Y 和小 Z 恰好在同一个节点。注意小 Y 只能在节点上抓到小 Z,而不能在边上抓住小 Z。
他们一共进行了 \(q\) 次追逐游戏,对于每次游戏,如果小 Y 都使用最优策略,请你计算小 Y 最早什么时刻抓住小Z,以及在哪个节点抓住小 Z。可以证明,在最优策略下,题目所求的时间和节点都唯一。
赛时
写了一坨,但反正能过。
题解
不难发现,小 Y 会先走到小 Z 的路径上,然后再分情况讨论小 Y 和小 Z 是追及问题还是相遇问题。
所以我们就需要一个能快速求 LCA、快速得到 \(k\) 级祖先的东西——倍增 LCA。
然后就是暴力分讨就行了。
变量名很长,但如果认真读的话,应该是能读懂的。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;
struct edge{int to,nxt;}e[400010];int head[200010],cnt=1;
inline void addedge(int x,int y){e[cnt]={y,head[x]},head[x]=cnt++;}
int n,q;
int fa[200010][20],dep[200010],lgn[200010];
void dfs(int x,int p){
fa[x][0]=p;
dep[x]=dep[p]+1;
for(int i=1;i<=lgn[dep[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==p) continue;
dfs(y,x);
}
}
void LCAinit(){
lgn[0]=-1;
for(int i=1;i<=n;i++) lgn[i]=lgn[i>>1]+1;
dfs(1,0);
}
int getlca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--) if(dep[y]<=dep[x]-(1<<i)) x=fa[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int getkfa(int x,int k){
int cnt=0;
while(k){
if(k&1) x=fa[x][cnt];
k>>=1;
cnt++;
}
return x;
}
pair<int,int> getans(int s1,int s2){
int lca_s1_s2=getlca(s1,s2);
int dis_s1_lca_s1_s2=dep[s1]-dep[lca_s1_s2];
int dis_lca_s1_s2_s2=dep[s2]-dep[lca_s1_s2];
int dis_s1_s2=dis_s1_lca_s1_s2+dis_lca_s1_s2_s2;
int mintime=(dis_s1_s2+1)/2;
int ans=0;
if(mintime<=dis_s1_lca_s1_s2) ans=getkfa(s1,mintime);
else ans=getkfa(s2,dis_lca_s1_s2_s2-(mintime-dis_s1_lca_s1_s2));
return {mintime,ans};
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<n;i++){
int x,y;cin>>x>>y;
addedge(x,y);
addedge(y,x);
}
LCAinit();
while(q--){
int s1,t,s2;cin>>s1>>t>>s2;
int lca_s1_t=getlca(s1,t);
int lca_lca_s1_t_s2=getlca(lca_s1_t,s2);
int lca_s1_s2=getlca(s1,s2);
int lca_t_s2=getlca(t,s2);
if(lca_lca_s1_t_s2==lca_s1_t&&(lca_s1_s2==s2||lca_t_s2==s2)){
pair<int,int> ans=getans(s1,s2);
cout<<ans.first<<" "<<ans.second<<"\n";
}else{
int dis_s2_path=0;
if(lca_lca_s1_t_s2!=lca_s1_t){
dis_s2_path=dep[lca_s1_t]+dep[s2]-dep[lca_lca_s1_t_s2]*2;
s2=lca_s1_t;
}else{
int dis_lca_s1_s2_s2=dep[s2]-dep[lca_s1_s2];
int dis_lca_t_s2_s2=dep[s2]-dep[lca_t_s2];
if(dis_lca_s1_s2_s2<dis_lca_t_s2_s2){
dis_s2_path=dis_lca_s1_s2_s2;
s2=lca_s1_s2;
}else{
dis_s2_path=dis_lca_t_s2_s2;
s2=lca_t_s2;
}
}
int dis_s1_lca_s1_t=dep[s1]-dep[lca_s1_t];
int dis_lca_s1_t_t=dep[t]-dep[lca_s1_t];
if(dis_s2_path<=dis_s1_lca_s1_t) s1=getkfa(s1,dis_s2_path);
else s1=getkfa(t,max(dis_lca_s1_t_t-(dis_s2_path-dis_s1_lca_s1_t),0));
lca_s1_t=getlca(s1,t);
lca_s1_s2=getlca(s1,s2);
lca_t_s2=getlca(t,s2);
lca_lca_s1_t_s2=getlca(lca_s1_t,s2);
if(lca_lca_s1_t_s2==lca_s1_t&&(lca_s1_s2==s2||lca_t_s2==s2)){
pair<int,int> ans=getans(s1,s2);
cout<<ans.first+dis_s2_path<<" "<<ans.second<<"\n";
}else{
int dis_t_s2=dep[s2]+dep[t]-dep[lca_t_s2]*2;
cout<<dis_s2_path+dis_t_s2<<" "<<t<<"\n";
}
}
}
# ifndef ONLINE_JUDGE
cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
# endif
return 0;
}
总结
不是为啥我 rank1 啊???
感觉写一些自己比较熟悉的部分,或者是认真想了细节的基本都是一遍写对)
不会 T3 T4。
欸我草一个总结给我写了 15KB。几乎全是我的超长史山贡献的。

浙公网安备 33010602011771号