其他分享
首页 > 其他分享> > Codeforces - 1327E - Count The Blocks(组合数学)

Codeforces - 1327E - Count The Blocks(组合数学)

作者:互联网

题目链接
题目大意:给你一个数字\(n\),问你在\(000...000\)到\(999...999\)(\(10^{n-1}\)位)里面长度为\(1,2...n\)里的块有多少(相等的连续子段算一块)。
  我们可以尝试构造出长度为i的块,对于一个长度为i的块来说:
  1.若它在数字的边缘,那么它右边(或左边)的第一个数一定与块内的数不同,又因为这个块可以在左边缘也可以在右边缘,所以这\(i+1\)个数一共有\(10\times 9 \times 2\)种情况,而剩下的部分有\(n-i-1\)位,所以就有
\(10^{n-i-1}\)种情况。所以一共是\(10\times 9 \times 2 \times 10^{n-i-1}\)种情况。
  2.若他不在数字的边缘,那么它的左右两边都有不同的数字,所以这\(i+2\)这个数就有\(10\times 9\times 9\)种情况,而剩下的部分有\(n-i-2\)位,所以就有\(10^{n-i-2}\)种情况。但是还没完,这个块还可以平移,每次平移
一位,我们的情况数就会增加,因为我们的一个块带上两边一共有\(i+2\)的长度,所以一共可以平移\(n-(i+2)\)次,再算上本身的这一次,一共是\(10\times 9\times 9\times 10^{n-i-2}\times (n-(i+2)+1)\)种情况。
  关于会不会算多或者算少的问题,我们每次算的只是所有数中长度为\(i\)的一部分,这些部分累加起来才是一个完整的数,所以不存在一个完整的数被多次计算的情况,也不会有一个完整的数没有被完全计算的情况。

const int maxn = 2e5+10;
ll ans[maxn];
ll qpow(ll x, int y) {
    ll res = x%MOD, ans = 1;
    while(y) {
        if (y&1) ans = ans*res%MOD;
        res = res*res%MOD;
        y >>= 1;
    }
    return ans%MOD;
}
int main(void) {
    ll n;
    scanf("%lld", &n);
    ans[n] = 10, ans[n-1] = 180;
    for (ll i = 1; i<=n-2; ++i)
        ans[i] = (ans[i] + 10LL*9*2*qpow(10, n-i-1)%MOD + 10LL*9*9*(n-i-1)%MOD*qpow(10, n-i-2))%MOD;
    for (int i = 1; i<=n; ++i)
        printf(i==n ? "%lld\n" : "%lld ", ans[i]);
    return 0;
}

标签:Count,10,1327E,Blocks,ll,times,一共,ans,MOD
来源: https://www.cnblogs.com/shuitiangong/p/12567270.html