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。几乎全是我的超长史山贡献的。

posted @ 2025-11-10 15:58  AeeE5x  阅读(9)  评论(0)    收藏  举报