gym103069 A. Namomo Subsequence(dp+容斥)

题意:

在这里插入图片描述

解法:

题 意 其 实 就 是 要 你 计 算 有 多 少 个 形 如 C D A B A B 的 子 序 列 . 考 虑 O ( n ) 枚 举 A , 那 么 方 案 数 为 左 边 的 C D 数 量 乘 上 右 边 的 B A B 数 量 . 右 边 B A B 数 量 可 以 先 计 算 A B 数 量 , 利 用 A B 数 量 计 算 B A B 数 量 . 左 边 C D 数 量 = X Y 数 量 − A X − B X − X A − X B + A B + B A . A B + B A = 左 边 A 的 数 量 乘 上 左 边 B 的 数 量 . 左 边 X Y 数 量 啥 的 可 以 O ( n ∗ 60 ) 利 用 d p 预 处 理 . 算 法 总 复 杂 度 O ( n ∗ 60 ) . 题意其实就是要你计算有多少个形如CDABAB的子序列.\\ 考虑O(n)枚举A,那么方案数为左边的CD数量乘上右边的BAB数量.\\ 右边BAB数量可以先计算AB数量,利用AB数量计算BAB数量.\\ 左边CD数量=XY数量-AX-BX-XA-XB+AB+BA.\\ AB+BA=左边A的数量乘上左边B的数量.\\ 左边XY数量啥的可以O(n*60)利用dp预处理.\\ 算法总复杂度O(n*60). CDABAB.O(n)ACDBAB.BABABABBAB.CD=XYAXBXXAXB+AB+BA.AB+BA=AB.XYO(n60)dp.O(n60).

code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxm=1e6+5;
const int mod=998244353;
char s[maxm];
int a[maxm];
int sum1[66][66];//AB的数量.
int sum2[66][66];//BAB的数量.
int cnt[66];
int f1[maxm][66];//[1,i]以Y结尾的XY数量.
int f2[maxm][66];//[1,i]以X开头的XY数量.
int cc[maxm][66];//[1,i]中X的数量.
int tot[maxm];//[1,i]中XY的总数量.
int n;
int cal(char c){
    if(c>='0'&&c<='9')return c-'0';
    else if(c>='a'&&c<='z')return c-'a'+10;
    else return c-'A'+36;
}
inline void add(int &a,int b){
    a+=b;
    while(a>=mod)a-=mod;
    while(a<0)a+=mod;
}
inline void DP(){
    int ans=0;
    for(int i=n;i>=3;i--){
        for(int j=0;j<62;j++){
            if(j==a[i])continue;
            add(sum2[a[i]][j],sum1[j][a[i]]);
            add(sum1[a[i]][j],cnt[j]);
        }
        cnt[a[i]]++;
        for(int j=0;j<62;j++){
            if(j==a[i])continue;
            int temp=tot[i-1];
            add(temp,-f1[i-1][a[i]]);
            add(temp,-f1[i-1][j]);
            add(temp,-f2[i-1][a[i]]);
            add(temp,-f2[i-1][j]);
            add(temp,1ll*cc[i-1][a[i]]*cc[i-1][j]%mod);
            add(ans,1ll*sum2[j][a[i]]*temp%mod);
        }
    }
    printf("%d\n",ans);
}
inline void solve(){
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)a[i]=cal(s[i]);
    for(int i=1;i<=n;i++){
        for(int j=0;j<62;j++){
            cc[i][j]=cc[i-1][j];
            f1[i][j]=f1[i-1][j];
            f2[i][j]=f2[i-1][j];
        }
        cc[i][a[i]]++;
        for(int j=0;j<62;j++){
            if(j==a[i])continue;
            add(f1[i][a[i]],cc[i-1][j]);
            add(f2[i][j],cc[i-1][j]);
        }
        for(int j=0;j<62;j++){
            add(tot[i],f1[i][j]);
        }
    }
    DP();
}
signed main(){
    solve();
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值