codeforces 856C Eleventh Birthday

D e s c r i p t i o n Description Description

给 定 n 个 数 , 求 将 这 n 个 数 按 拼 在 一 起 形 成 一 个 大 数 , 这 个 大 数 是 是 11 的 倍 数 的 方 案 数 。 给定n个数,求将这n个数按拼在一起形成一个大数,这个大数是\\是11的倍数的方案数。 nn11

S o l u t i o n Solution Solution

对 于 这 题 , 我 们 首 先 得 想 到 一 个 数 学 性 质 对于这题,我们首先得想到一个数学性质
10 ≡ − 1 ( m o d 11 ) 10 \equiv -1 (mod 11) 101(mod11)
对 于 这 个 性 质 , 比 如 a , b , c 三 个 数 拼 接 成 a b c 形 式 , a b c = a ∗ 1 0 l e n ( b + c ) + b ∗ 1 0 l e n ( c ) + c 若 满 足 a ∗ 1 0 l e n ( b + c ) + b ∗ 1 0 l e n ( c ) + c ≡ 0 ( m o d 11 ) 则 可 化 为 a ∗ ( − 1 ) l e n ( b + c ) + b ∗ ( − 1 ) l e n ( c ) + c ≡ 0 ( m o d 11 ) 即 是 a , b , c 本 身 ∗ 各 自 所 在 的 奇 偶 位 是 11 的 倍 数 时 , 成 立 ( 举 例 , a = 9 , b = 2 , c = 4 , 组 成 的 数 9 ∗ 1 + 2 ∗ − 1 + 4 ≡ 0 , 故 924 是 11 的 倍 数 ) 对于这个性质,比如 a,b,c三个数拼接成abc形式,\\ abc=a*10^{len(b+c)}+b*10^{len(c)}+c\\ 若满足a*10^{len(b+c)}+b*10^{len(c)}+c \equiv 0 (mod 11)\\ 则可化为a*(-1)^{len(b+c)}+b*(-1)^{len(c)}+c \equiv 0 (mod 11)\\ 即是a,b,c本身*各自所在的奇偶位是11的倍数时,成立\\ (举例,a=9,b=2,c=4,组成的数9*1+2*-1+4 \equiv 0 ,故924是11的倍数) a,b,cabcabc=a10len(b+c)+b10len(c)+ca10len(b+c)+b10len(c)+c0(mod11)a(1)len(b+c)+b(1)len(c)+c0(mod11)a,b,c11(,a=9,b=2,c=4,91+21+4092411)

我 们 就 若 设 a ∗ 1 , a 在 奇 数 位 , a ∗ − 1 , a 在 偶 数 位 , 那 么 我 们 现 在 只 需 要 去 找 哪 些 数 在 偶 数 位 , 哪 些 数 在 奇 数 位 的 情 况 , 在 数 列 中 , 我 们 插 入 一 个 l e n 为 偶 数 的 数 字 , 不 会 改 变 后 续 的 奇 偶 位 情 况 情 况 , 而 插 入 一 个 l e n 为 奇 数 的 则 会 全 部 反 过 来 我们就若设a*1,a在奇数位,a*-1,a在偶数位,那么我们现在只需要去找\\ 哪些数在偶数位,哪些数在奇数位的情况,在数列中,我们插入一个len为\\ 偶数的数字,不会改变后续的奇偶位情况情况,而插入一个len为奇数的则会全部\\ 反过来 a1aa1,a,lenlen
我 们 把 所 有 l e n 为 奇 数 的 数 统 计 出 来 , 设 置 这 些 数 在 奇 数 位 或 偶 数 位 的 时 候 , 和 为 k 时 有 多 少 方 案 , 那 么 剩 下 的 l e n 为 偶 数 的 数 , 直 接 插 入 就 好 了 , 不 会 影 响 数 字 本 身 所 在 的 奇 偶 位 , 设 l e n 为 奇 数 的 数 有 n 1 个 , 命 名 为 a i , l e n 为 偶 数 有 n 2 个 , 命 名 为 b i 我们把所有len为奇数的数统计出来,设置这些数在奇数位或偶数位的时候,和为k时\\ 有多少方案,那么剩下的len为偶数的数,直接插入就好了,不会影响数字本身所在的\\ 奇偶位,设len为奇数的数有n1个,命名为a_i,len为偶数有n2个,命名为b_i lenklen,lenn1,ai,lenn2bi
设 f [ i ] [ j ] [ k ] 为 l e n 为 奇 数 中 前 i 个 数 , 有 j 个 数 在 偶 数 位 时 , 和 为 k 的 方 案 有 多 少 设f[i][j][k]为len为奇数中前i个数,有j个数在偶数位时,和为k的方案有多少 f[i][j][k]leni,j,k
设 g [ i ] [ j ] [ k ] 为 l e n 为 偶 数 中 前 i 个 数 , 有 j 个 数 在 偶 数 位 时 , 和 为 k 的 方 案 有 多 少 设g[i][j][k]为len为偶数中前i个数,有j个数在偶数位时,和为k的方案有多少 g[i][j][k]leni,j,k
l e n 为 奇 数 的 最 终 状 态 一 定 时 f [ n 1 ] [ n 1 / 2 ] [ 0..10 ] , l e n 为 偶 数 可 以 任 意 len为奇数的最终状态一定时f[n1][n1/2][0..10],len为偶数可以任意 lenf[n1][n1/2][0..10],len
状态转移方程为
f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k − a [ i ] ] + f [ i − 1 ] [ j − 1 ] [ k + a [ i ] ] f[i][j][k]=f[i−1][j][k−a[i]] + f[i−1][j−1][k+a[i]] f[i][j][k]=f[i1][j][ka[i]]+f[i1][j1][k+a[i]]
g [ i ] [ j ] [ k ] = g [ i − 1 ] [ j ] [ k − b [ i ] ] + g [ i − 1 ] [ j − 1 ] [ k + b [ i ] ] g[i][j][k]=g[i-1][j][k-b[i]] + g[i-1][j-1][k+b[i]] g[i][j][k]=g[i1][j][kb[i]]+g[i1][j1][k+b[i]]
而 f [ n 1 ] [ n 1 / 2 ] [ 0..10 ] 对 于 每 一 个 只 是 表 示 n 1 / 2 个 偶 数 下 , 但 对 于 每 个 具 体 位 置 , n 1 / 2 个 位 于 偶 数 位 的 数 和 n 1 − n 1 / 2 个 位 于 奇 数 位 的 数 是 可 以 互 相 换 位 的 , 故 还 需 乘 以 ( n 1 / 2 ) ! ∗ ( n 1 − n 1 / 2 ) ! 再 思 考 , 对 于 l e n 为 偶 数 中 的 情 况 , g [ n 2 ] [ j ] [ k ] 中 , 有 j 个 数 在 偶 数 位 , 那 么 这 偶 数 位 得 位 于 n 1 − n 1 / 2 个 数 之 后 , 变 成 了 一 个 组 合 数 问 题 , j 个 球 放 在 n 1 − n 1 / 2 个 盒 子 里 , 一 个 盒 子 可 放 任 意 多 个 球 , 盒 子 可 以 为 空 , 即 是 多 重 集 组 合 数 , j 个 球 可 任 意 交 换 , 则 再 乘 j ! , 对 于 n 2 − j 个 放 在 奇 数 为 得 数 同 理 , 只 是 奇 数 位 多 了 一 个 第 一 位 可 以 放 , 则 是 n 2 − j 个 球 放 在 n 1 / 2 + 1 个 盒 子 O K , 完 全 s o l v e d ! ! 而f[n1][n1/2][0..10]对于每一个只是表示n1/2个偶数下,但对于每个具体位置, n1/2个位于偶数位的数和n1-n1/2个位于奇数位的数是可以互相换位的,故还需乘以\\ (n1/2)!*(n1-n1/2)!\\ 再思考,对于len为偶数中的情况,g[n2][j][k]中,有j个数在偶数位,那么这偶数位\\ 得位于n1-n1/2个数之后,变成了一个组合数问题,j个球放在n1-n1/2个盒子里,一个\\ 盒子可放任意多个球,盒子可以为空,即是多重集组合数,j个球可任意交换,\\ 则再乘j!,对于n2-j个放在奇数为得数同理,只是奇数位多了一个第一位可以放,\\ 则是n2-j个球放在n1/2+1个盒子\\ OK,完全solved!! f[n1][n1/2][0..10]n1/2,,n1/2n1n1/2,(n1/2)!(n1n1/2)!leng[n2][j][k]jn1n1/2,jn1n1/2jj!,n2j,n2jn1/2+1OK,solved!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//#pragma GCC optimize(3, "Ofast", "inline")
#define fir(i, a, b) for (int i = a; i <= b; i++)
#define mov(i) (1ll << i)
#define MAX_INF 0x3f3f3f3f
ll qpow(ll a, ll b, ll p)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1)
            ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}
const int maxn = 2e3 + 10;
int a[maxn],b[maxn];
int mod = 998244353;
int f[2][maxn][12],g[2][maxn][12];//直接开数组MLE,状态压缩以下
inline int add(ll a,ll b)
{
    return (a+b+mod)%mod;
}
ll fac[maxn], invfac[maxn], t[maxn];
void init(int n, ll MOD)
{
    fac[0] = 1;
    mod = MOD;
    for (int i = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % mod;
    invfac[n] = qpow(fac[n], mod - 2, mod);
    for (int i = n; i >= 1; i--)
        invfac[i - 1] = invfac[i] * i % mod;
}
ll C(ll n, ll m)//求组合数
{
    return n >= m ? fac[n] * invfac[n - m] % mod * invfac[m] % mod : 0;
}
//多重集排列数
ll cal(ll n,ll m)
{
    if(!m) return (n==0);//n,m可能均为0
    return fac[n]*C(n+m-1,m-1)%mod;
}
int main()
{
  //  input();
    int T;
    init(maxn-1,mod);
    fac[0]=1;
    cin >> T; 
    while(T--)
    {
        int n;
        cin>>n;
        int cnta=0,cntb=0;
        fir(i,1,n)
        {
            int x;
            scanf("%d",&x);
            if(to_string(x).size()&1)
            a[++cnta]=x%11;
            else 
            b[++cntb]=x%11;
        }
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        f[0][0][0]=1;g[0][0][0]=1;
        for(int i=1;i<=cnta;i++)
        {
            int curi=i&1,lasti=!(i&1);
            for(int j=0;j<=i;j++)
            {
                fir(k,0,10)
                {
                    f[curi][j][k]=add(f[curi][j][k],f[lasti][j][(k-a[i]+11)%11]);
                    if(j)
                    f[curi][j][k]=add(f[curi][j][k],f[lasti][j-1][(k+a[i])%11]);
                }
            }
            memset(f[lasti],0,sizeof(f[lasti]));
        }
        
        for(int i=1;i<=cntb;i++)
        {
            int curi=i&1,lasti=!(i&1);
            for(int j=0;j<=i;j++)
            {
                fir(k,0,10)
                {
                    g[curi][j][k]=add(g[curi][j][k],g[lasti][j][(k-b[i]+11)%11]);
                    if(j)
                    g[curi][j][k]=add(g[curi][j][k],g[lasti][j-1][(k+b[i])%11]);
                }
            }
            memset(g[lasti],0,sizeof(g[lasti]));
        }
        ll ans=0;
        ll temp=fac[cnta/2]*fac[cnta-cnta/2]%mod;
        int m=cnta/2+1;
       // cout<<"temp:"<<temp<<endl;
        for(int i=0;i<=cntb;i++)
        {
            for(int j=0;j<11;j++)
            {
                int p=cntb-i;
                ll val=(ll)g[cntb&1][i][j]*f[cnta&1][cnta/2][(11-j)%11]%mod*temp%mod*cal(p,m)%mod*cal(i,cnta-cnta/2)%mod;
                ans=(ans+val)%mod;
            }
        }
        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值