2024牛客寒假算法基础集训营3

文章详细描述了智乃在不同编程题目中的应用,包括字符串哈希处理、判断字母和数字序列的关系、回文判断、排序算法、数学博弈和构造特殊数列,展示了她在解决复杂问题时的技术策略。
摘要由CSDN通过智能技术生成

目录

A.智乃与瞩目狸猫、幸运水母、月宫龙虾

B.智乃的数字手串

C.智乃的前缀、后缀、回文

D.chino's bubble sort and maximum subarray sum(easy version)

G,H.智乃的比较函数(easy & hard)

J.智乃的相亲活动

K.智乃的“黑红树”

L.智乃的36倍数(easy version)

M.智乃的36倍数(hard version)


A.智乃与瞩目狸猫、幸运水母、月宫龙虾

阅读理解题,判断两个字母的第一个字母在忽视大小写情况下是否相等我们可以使用tolower函数

void solve(){
   
    string a,b; cin>>a>>b;
    cout<<(tolower(a[0])==tolower(b[0]) ? "Yes" : "No" )<<endl;
    return ;
}

B.智乃的数字手串

对于简单博弈的话我们可以自己手捏数据然后大胆猜结论

这里给出推理最后的状态一定是 奇偶奇偶奇偶  也就是偶数状态我们可以发现只要一开始是奇数个数一定是必胜的奇数,她会转移到必败态偶(题目说了1的时候可以直接拿走所以符合)

void solve(){
   
    cin>>n;
    vector<int> op(2);
    for(int i=1;i<=n;i++){
        int x; cin>>x;
    }
    if(n&1)cout<<"qcjj"<<endl;
    else cout<<"zn"<<endl;
    return ;
}

C.智乃的前缀、后缀、回文

首先明确题目意思 也就是找两个前后缀同时满足要是相同且回文,对于相同的话我们可以使用hash,对于回文的话我们也可以使用倒过来hash,所以这个题就是对字符串进行hash处理即可

我们考虑使用双hash模板避免出现hash冲突,同时先处理a的前缀和b的后缀匹配,再处理b的前缀和a的后缀我们要满足没有重叠即可

LL p[N][2],ha[N][2],hb[N][2],ha1[N][2],hb1[N][2];
struct Hash{
    const int P0=1e9+3,P1=1e9+7,base=131;
    void intn(int n){
         p[0][0]=p[0][1]=1;
         for(int i=1;i<=max(n,m);i++){
            p[i][0]=p[i-1][0]*base%P0;
            p[i][1]=p[i-1][1]*base%P1;
         }
    }
    void get(LL h[][2],string a,int n){
        for(int i=1;i<=n;i++){
            h[i][0]=(h[i-1][0]*base+a[i])%P0;
            h[i][1]=(h[i-1][1]*base+a[i])%P1;
        }
    }
    pair<LL,LL> query(LL h[][2],int l,int r){
      LL res1=((h[r][0]-h[l-1][0]*p[r-l+1][0]%P0)%P0+P0)%P0; 
      LL res2=((h[r][1]-h[l-1][1]*p[r-l+1][1]%P1)%P1+P1)%P1; 
      return {res1,res2};
    }
}hx;
void solve(){
    
    hx.intn(N-5);
    cin>>n>>m;
    string a,b ;cin>>a>>b;
    a='$'+a+'$',b='$'+b+'$';
    hx.get(ha,a,n);
    hx.get(hb,b,m);
    reverse(a.begin(),a.end());
    reverse(b.begin(),b.end());
    hx.get(ha1,a,n);
    hx.get(hb1,b,m);
    vector<int> pre(n+5),suf(n+5);
    vector<int>res1,res2;
    for(int len=1;len<=min(n,m);len++){
       if(hx.query(ha,1,len)==hx.query(ha1,n-len+1,n) and hx.query(hb,m-len+1,m)==hx.query(hb1,1,len)){
            if(hx.query(ha,1,len)== hx.query(hb1,1,len))res1.push_back(len);
       } 
    }
    for(int len=1;len<=min(n,m);len++){
       if(hx.query(hb,1,len)==hx.query(hb1,m-len+1,m) and hx.query(ha,n-len+1,n)==hx.query(ha1,1,len)){
            if(hx.query(hb,1,len)== hx.query(ha1,1,len))res2.push_back(len);
       } 
    }
    int res=-1;
    for(auto &v:res1){
        while(res2.size() and v+res2.back()>min(n,m))res2.pop_back();
        if(res2.size())res=max(res,(v+res2.back())*2);
    }
    cout<<res<<endl;
    return ;
}

D.chino's bubble sort and maximum subarray sum(easy version)

考虑只需要交换一个数对于一个区间[l,r]假设就是周围最大的了,考虑数据范围很小所以明显的交换每一个区间的端点即可,可以简单证明是正确的

void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=b[i-1]+a[i];
    }
    LL ans=-2e18;
     
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            ans=max(ans,b[j]-b[i-1]);
    if(m){
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++){
                if(i>=3){
                    ans=max(ans,b[j]-b[i-1]+a[i-2]);
                }
                if(j+2<=n){
                     ans=max(ans,b[j]-b[i-1]+a[j+2]);
                }
            }
    }
    cout<<ans<<endl;
    return ;
}

G,H.智乃的比较函数(easy & hard)

我们可以发现这是多个关系之间的问题,维护关系我们可以使用传递闭包,也就是floy,同时我们理清楚每一个关系的意思 

cmp(x,y) 1的意思是严格小于也就是   x <y

cmp(x,y) 0的意思是 x<=y

所以依照这两点来传递关系

 1.a<=b,b<=c -->a <=c

2.a<=b,b<c -->a<c

3.a<=b,a>=b -->a==b

然后矛盾就是

a<b,a>b

a==b (a>b || b<a)

这些关系的存在 所以按照要求即可可以用三维数组表示关系

int op[4][4][2];

void solve(){
   
    cin>>n;
    memset(op,0,sizeof op);
    for(int i=1;i<=n;i++){
        int x,y,z; cin>>x>>y>>z;
        op[x][y][z]++;
        if(z==1) op[y][x][0]++;
    }
    for(int i=1;i<=3;i++) op[i][i][0]++;
    
    for(int k=1;k<=3;k++)
      for(int i=1;i<=3;i++)
         for(int j=1;j<=3;j++){
             if(op[i][k][1] && (op[k][j][1] || op[j][k][0])){
                 op[i][j][1]++;
                 op[j][i][0]++;
             }
             if(op[i][k][0] && op[k][i][0]  && op[k][j][0] && op[j][k][0]){
                 op[i][j][0]++;
                 op[j][i][0]++;
             }
         }
    bool ok=true;
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(op[i][j][1] && op[i][j][0]) ok=false;
            if(op[i][j][1] && op[j][i][1]) ok=false;
            if(op[i][j][0] && op[j][i][0] && (op[i][j][1]||op[j][i][1])) ok=false;
        }
    cout<<(ok ? "Yes" : "No")<<endl;
    return ;
}

J.智乃的相亲活动

数学逻辑推理题

计算贡献,假设我们真的按照题目的意思直接求答案的我们会发现压根无法动手因为选择的互相干扰性导致我们无法定义状态以及关系之间的传递,这个时候我们要使用数学中常用的解题方式正难则反,对于每一个人而言他的贡献是1,他被选择的概率是多少呢?我们可以得到,1-所有人都不选他,就是他被选的概率所以他最后的期望贡献就是1*他被选的概率

std::vector<int> v1[N],v2[N];
void solve(){
    int n,m,k;
    cin>>n>>m>>k;

    for(int i=1;i<=k;i++){
        int a,b;
        cin>>a>>b;
        v1[a].push_back(b);
        v2[b].push_back(a);
    }
    double ans1=0;
    for(int i=1;i<=n;i++){
        double now=1;
        for(auto c:v1[i]){
            now*=(1.0-1.0/v2[c].size());
        }
        ans1+=1.0-now;
    }
    double ans2=0;
    for(int i=1;i<=m;i++){
        double now=1;
        for(auto c:v2[i]){
            now*=(1.0-1.0/v1[c].size());
        }
        ans2+=1.0-now;
    }
    std::cout << std::fixed << std::setprecision(8) << "float \n" << ans1 <<  ' ' << ans2;
}

K.智乃的“黑红树”

我们可以发现这是一个构造题那么就先找性质可以简单得出

1 黑色一定是奇数   红色一定是偶数
2 红色不超过黑色的两倍 黑色最多的时候是红色两倍加一

由此我们才开始构造,一开始就是黑色的那么我们就把能构造的黑色都构造然后构造红色即可多次操作即可得到答案

void solve(){
   
    int a,b; cin>>a>>b;
    queue<int> black,red;
    if(a%2==0 || a>b*2+1 || b>2*a || b&1) return cout<<"No"<<endl,void();
    
    vector<PII> res(a+b+5);
    black.push(1);
    a--;
    int now=1;
    while(a || b){
        while(!black.empty() && b){
             int t=black.front(); black.pop();
             res[t]={now+1,now+2};
             red.push(now+1);
             red.push(now+2);
             now+=2,b-=2;
        }
        while(!red.empty() && a){
             int t=red.front(); red.pop();
             res[t]={now+1,now+2};
             black.push(now+1);
             black.push(now+2);
             now+=2,a-=2;
        }
    }
    cout<<"Yes"<<endl;
    for(int i=1;i<=now;i++){
        auto [a,b]=res[i];
        if(!a) cout<<-1<<' '<<-1<<endl;
        else cout<<a<<' '<<b<<endl;
    }
    return ;
}

L.智乃的36倍数(easy version)

对于这种easy问题一般结论都很显然所以我们可以直接n^2拼接即可然后用to_string函数快速求出位数

int a[N];

void solve(){
   
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) continue;
            int x=to_string(a[j]).size();
            int now=a[i]*pow(10,x)+a[j];
            if(now%36==0) ans++;
        }
    }
    cout<<ans<<endl;
    return ;
}

M.智乃的36倍数(hard version)

这依旧是两个数的缝合,我们考虑缝合后对这个模数的影响是什么,由于只有10e18也就是最多是18位数那么每一个数后面都最多接上18位数,我们可以把合成的数看成两个部分前面和后面,这个时候我只需要对每一个数合并多少位数的取模值得到即可,接着来计算多少的贡献同时注意题目要求不能自己接在自己身上所以需要特判,数字拼接题都是如此的性质

LL a[N];
LL cnt[25][40];
LL pw[25];
void solve(){
    
    pw[0]=1;
    for(int i=1;i<=20;i++){
        pw[i]=pw[i-1]*10%mod;
    }
    cin>>n;
    for(int i=1;i<=n;i++){
         cin>>a[i];
         for(int j=1;j<=20;j++){
             cnt[j][(a[i]%mod*pw[j]%mod)]++;
         }
    }
    LL ans=0;
    for(int i=1;i<=n;i++){
        LL x=a[i];
        int len=to_string(x).size();
        x%=36;
        ans+=cnt[len][(36-x)%36];
        int let=((LL)a[i]%mod*pw[len]%mod+x)%mod;
        if(!let) ans--;
    }
    cout<<ans<<endl;
     
    return ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值