2021ccpc网络赛 Nun Heh Heh Aaaaaaaaaaa

字符串子序列与排列组合
该博客主要讨论字符串子序列的计数问题,特别是寻找具有特定前缀和后缀(非零数字)的子序列数量。作者介绍了动态规划(DP)方法来解决这个问题,并给出了代码实现,特别强调了在状态转移过程中防止整型溢出的问题。博客内容涉及字符串处理、动态规划和组合数学。

Problem Description

Vasily Tadokorov is a stringologist. He thinks a string is fragrant if it can be divided into two parts — nunhehheh as the prefix and a number of (excluding 0) a as the suffix. For example, nunhehhehaaaaaa is fragrant, but nunhehheh and nunhehhehoooaaa are not fragrant.

Today Vasily Tadokorov has some strings consisting of lowercase English letters. For each string, he wants to know how many subsequences of this string are fragrant. A string a is a subsequence of a string b if a can be obtained from b by deletion of several (including 0) characters.

Input

The first line contains an integer T (1≤T≤1000), denoting the number of strings.

Each of the next T lines contains a string S (1≤|S|≤105) consisting of lowercase English letters.

The total length of the strings in the input will not exceed 106.

Output

For each of the given T strings, output the answer modulo 998244353.



题解

dp[j][i] 表示s串中0 ~ i - 1中 子序列为 nunhehheh的0 ~ j - 1的个数,对于当前位置i求得新增加的子序列个数 (dp[9][i] - dp[9][i]) * i后面a的排列组合(2^(pre[s.size()] - pre[i]) - 1, (pre[s.size()] - pre[i])个 'a’每个选或者不选的方案 - 都不选的方案)

注意dp状态转移过程会爆long long 要边转移边取模,差值也要取模如
( dp[9][i] - dp[9][i - 1] + mod )%mod;,比赛的时候因为忘了这个wa惨了



code

#include <bits/stdc++.h>
using namespace std;
#define IOS std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

typedef long long ll;


ll mod = 998244353;
ll qq(ll b){
    ll ans = 1;
    ll a = 2;
    while(b > 0){
        if(b&1){
            ans = (ans * a) % mod;
        }
        a = (a * a)%mod;
        b >>= 1;
    }
    return ans;
}

string s,t = "nunhehheh";
             
const int N = 1E5 + 10;
ll dp[11][N];
ll pre[N];

int main()
{   
    IOS
    int T;  cin >> T;
    for(int k = 1; k <= T; ++k){
        cin >> s;
        int sz = s.size(),tz = t.size();
        for(int i = 1; i <= sz; ++i){
            pre[i] = 0;
            if(s[i-1] == 'a')
                pre[i] = 1;
            pre[i] += pre[i - 1];
        }

        ll ans = 0;
        memset(dp,0,sizeof dp);
      
        for(int i = 0; i <= sz; ++i)
            dp[0][i] = 1;
        for(int i = 1; i <= sz; ++i)
        {
            for(int j = 1; j <= tz; ++j)
            {
                /* 根据是否相等执行不同操作,因为此时需要匹配t[0 : j],而只有相等是才可以允许只匹配t[0 : j-1] */
                if(s[i - 1] == t[j - 1])
                    dp[j][i] = (dp[j - 1][i - 1] + dp[j][i-1])%mod;
                else
                    dp[j][i] = dp[j][i-1];
            }
            ll x = pre[sz] - pre[i];
            ll y = ( dp[tz][i] - dp[tz][i - 1] + mod )%mod;
            y%=mod;
            if( y && x){    
              
                y =  y * ( ( qq(x) - 1 + mod )%mod ) % mod;
                ans += y;
                ans %= mod;
            }
            
        }
        cout << ans << '\n';

    }
      
      return 0;
}
不同年份的CCPC网络有不同的题目及解答: - 2018年CCPC网络:给一张竞图,对于所有的4元组,若为四元环贡献是1,有两个点的出度是2则贡献是 -1。先假设所有四元组都是四元环,这部分对答案的贡献是A(n,4)。对于非四元环的四元组,必然存在一个点出度是2,枚举每个度数>=2的点,其对答案的贡献是A(deg[i],2)*(n - 3)*4 ,所以最终答案ans = A(n,4) - ΣA(deg[i],2)*(n - 3)*4 ,这其实要求ΣC(deg[i],2),C(deg,2)=deg*(deg - 1)/2 ,当前度数为n,度数每增加1,对于这个点贡献增加n,可暴力连边跑费用流求解[^2]。 - 2021CCPC网络:发现规律,先将剩余的1或2或3构造出来,剩下的四个一组构造。代码实现如下: ```cpp #include<bits/stdc++.h> using namespace std; int main(void) { int t; cin>>t; while(t--) { int n; cin>>n; string ans; if(n%4==1) n=n-1,ans+="1"; else if(n%4==2) n=n-2,ans+="0001"; else if(n%4==3) n=n-3,ans+="01"; for(int i=1;i<=n/4;i++) ans+="1001"; cout<<ans.size()<<endl; cout<<ans<<endl; } return 0; } ``` 该题1009为Command Sequence,难度一般,知识点涉及前缀和与哈希[^4]。 - 2016年CCPC网络:一个星球一年有73天,另一个一年有137天,给定第n天,判断这一天是否同时是两个星球一年中的第一天,即判断n是否同时整除73和137,因n超大,使用紫书的大数模去一个较小的数的方法,代码如下: ```cpp #include <cstdio> #include <string> #include <cstring> #include <iostream> using namespace std; char n[10000010]; int m; int main(){ int m = 10001; int cas = 1; while(~scanf("%s",n)){ int len = strlen(n); int ans = 0; for(int i=0;i < len;i++){ ans = (int)(((long long)ans*10+n[i]-'0')%m); } printf("Case #%d: ",cas++); if (ans == 0) printf("YES\n"); else printf("NO\n"); } return 0; } ``` [^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值