XVIII GP of Urals. D题 Anna and Lucky Tickets

题意

求长度为 n n n的数字串方案数,满足奇数位和等于偶数位和,且是回文串

题解

首先考虑 n n n为偶数,此时左侧和右侧奇偶性相反,所以必然奇数和偶数的和相等。
n n n为奇数的时候:
枚举中间位是 k k k,且接下来的奇数偶数不考虑中间这一位。
【注意这里的 k k k,由于回文串,奇数和和偶数和都是偶数,所有差一定也是偶数】
如果中间是奇数,偶数 = = = 奇数 + k +k +k
反之,奇数 = = = 偶数 + k +k +k
显然前者的时候,奇数和偶数是一样的,所以我们可以都变成第二个问题。

奇数 − - 偶数 = k =k =k
显然这样计算方案数,我们仍然要考虑奇数和偶数,而这样是很难计算的。
如果我们能把 − - 变成 + + +,就可以只考虑如何放数了。
做法就是,奇数 + + +(( 9 − 9- 9偶数)和) = k + 9 =k+9 =k+9的和
就是把每个偶数位 x x x变成 9 − x 9-x 9x,这样 − x -x x就是 + x − 9 +x-9 +x9了,转换成了加法。
也就是转换成了 ∑ x i = k + e v e n ∗ 9 = m \sum x_i=k+even*9=m xi=k+even9=m

前一半和后一半一样,所以我们只用考虑前一半的数, n = n 2 , m = m 2 n=\frac{n}{2},m=\frac{m}{2} n=2n,m=2m
把一个 1 1 1当作一个小球,一位当作一个盒子,就是允许空盒的放小球方案数了,我们用隔板法, C ( m + n − 1 , n − 1 ) C(m+n-1,n-1) C(m+n1,n1)即方案数,但是有些情况某些位会超过 9 9 9,这就需要容斥了。

我们枚举超过的位数, a n s = ∑ ( − 1 ) i C ( n , i ) ∗ C ( M − 10 ∗ i + n − 1 , n − 1 ) ans=\sum(-1)^iC(n,i)*C(M-10*i+n-1,n-1) ans=(1)iC(n,i)C(M10i+n1,n1)即总方案数。
超过的位数为 i i i时,方案数即:我们将 i i i个盒子挑出来,都放上 10 10 10,这样就算之后空盒也是保证超过的, m m m去掉 10 ∗ i 10*i 10i,然后继续放带空盒的方案,就可以得到答案了。

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse3","sse2","sse")
//#pragma GCC target("avx","sse4","sse4.1","sse4.2","ssse3")
//#pragma GCC target("f16c")
//#pragma GCC target("fma","avx2")
//#pragma GCC target("xop","fma4")
#pragma GCC optimize("inline","fast-math","unroll-loops","no-stack-protector")
#pragma GCC diagnostic error "-fwhole-program"
#pragma GCC diagnostic error "-fcse-skip-blocks"
#pragma GCC diagnostic error "-funsafe-loop-optimizations"
#pragma GCC diagnostic error "-std=c++14"

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long ll;
const int maxn = 6e6+1000;
const ll mod = 1e9 + 7;

ll F[maxn],Finv[maxn],inv[maxn];

inline ll quick_pow(ll x,int p){
    ll res=1;
    while(p){
        if(p&1)res=(res*x)%mod;
        x=(x*x)%mod, p>>=1;
    }
    return res;
}

void init(){
    F[0]=Finv[0]=inv[1]=1ll;
    for(int i=2;i<maxn;i++){
        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    }
    for(int i=1;i<maxn;i++){
        F[i]=F[i-1]*i%mod;
        Finv[i]=Finv[i-1]*inv[i]%mod;
    }
}

ll C(ll n,ll m){
    if(m<0||m>n)return 0;
    return F[n]*1ll*Finv[n-m]%mod*Finv[m]%mod;
}

int main(){
    int n;cin>>n;
    init();
    if(n%2==0)return printf("%lld\n",quick_pow(10,n/2)),0;
    int odd=(n+1)/2,even=n/2;
    if(((n+1)/2)%2)odd--;
    else even--;
    ll m=even*9,ans=0;
    odd/=2,even/=2;
    for(int k=0;k<10;k++){
        ll tmp=(m+k)/2,res=0;
        if(k%2)continue;
        for(int i=0,n=odd+even;i<=n;i++){
            if(i%2)res=(res-C(n,i)*C(tmp-10*i+n-1,n-1))%mod;
            else res=(res+C(n,i)*C(tmp-10*i+n-1,n-1))%mod;
            res=(res+mod)%mod;
        }
        ans=(ans+res)%mod;
    }
    cout<<ans<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值