CodeForces 327C Magic Five 二分求和 或 矩阵求和

这是个快速幂的题吧,,刚开始交的时候一直wa在第5组测试数据,,表示1000000007这个东西,一定要找个时间练练1000000007相关的题 T_T


题目大意:

给你一串数字, 然后给出一个数字k,表示有k次重复,从这个数字串中去掉一些数(也可以不去掉任何数)要求剩下的数能被5整除,剩下的数可以有前导零,不同的去除方法即使得到相同的数字串也算不同的方法。 求可能的方法的数量,结果模上1000000007。


大致思路:

首先考虑不进行k次重复时,这个数字串要使得结尾是0或5,那么,在被作为最后以为的0或5之前的数可以去掉或者不去掉,在其之后的数必须都去掉。

那么,以一个数字串的第 i 位作为结尾的可能性就有 2 ^ i 种, 由于数字串重复,记字符串未重复时长度为 n, 那么数字串的第 i + n, i+ 2*n, i + 3*n ,,,,,, i + (k - 1)*n都可以作为结尾。

对于原数字串,第 i 位作为结尾, i + n, i + 2*n, i + 3*n, i + 4*n,,,,i + k*n 的可能方式有 (2 ^ i ) * (2^0 + 2^n + 2^(2*n) + 2^(3*n) + ....+ 2^((k - 1)*n); 

对于原数字串统计它的 2 ^ i 的总和就可以了。



这里给出两种做法

解法一:

将 (2^0 + 2^n + 2^(2*n) + 2^(3*n) + ....+ 2^((k - 1)*n) 每次二分变为:

((2^0 + 2^n + 2^(2*n) + 2^(3*n) + ....+ 2^((k - 1)/2)*n))  * (1 + 2 ^ ((((k - 1)/2) + 1)*n))

对于每个2的次方都用快速幂,代码如下:


Result : Accepted  Memory :  300 KB    Time :  62 ms

/*
 * Author : Gatevin
 * Time : 2014.4.9 22:07
 * Problem : CodeForces 327C Magic Five
 * Tag : quick_pow, math, geometric sequence
 */
 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
typedef long long lint;
using namespace std;
const int mod = 1000000007;
string s;
int times;

lint quick_pow(lint base, lint pow)//快速幂
{
    lint rep = 1;
    while(pow)
    {
        if(pow & 1)
        {
            rep = (rep * base)%mod;
        }
        pow >>= 1;
        base = (base*base)%mod;
    }
    return rep;
}

lint two_divide(lint base, lint len)//对于要求和的式子每次二分,左右两边有一个倍数关系,有可能多一项
{
    if(len == 1) return 1;
    lint tmp = two_divide(base, len >> 1);
    lint rep = ((1 + quick_pow(base, len >> 1))*tmp)%mod;
    if(len & 1)//多一项时
    {
        rep = (rep + quick_pow(base, len - 1))%mod;
    }
    return rep;
}

int main()
{
    cin>>s>>times;
    lint ans = 0;
    for(int i = 0; i <= s.size() - 1; i++)
    {
        if(s[i] == '0' || s[i] == '5')
        {
            ans = ( ans + quick_pow(2,i))%mod;//式子的一部分
        }
    }
    ans = (ans*two_divide(quick_pow(2,s.size()), times))%mod;//乘上另一部分
    cout<<ans<<endl;
    return 0;
}

解法二:

采用矩阵求等比数列的和

这个还是看经验问题吧,,,

Result : Accepted    Memory : 300 KB   Time : 62ms

/*
 * Author : Gatevin
 * Time : 2014.4.9  23:36
 * Problem : CodeForces 327C  Magic Five
 * Tag : Matrix, math, geometric progression
 */

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long lint;
const lint mod = 1000000007;
struct Matrix
{
    lint p[2][2];
    Matrix()
    {
        for(int i = 0; i < 2; i++)
        {
            for(int j = 0; j < 2; j++)
            {
                p[i][j] = (i == j) ? 1 : 0;
            }
        }
    }
};

Matrix Mutiple(Matrix A, Matrix B)
{
    Matrix C;
    for(int i = 0; i < 2; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            C.p[i][j] = 0;
            for(int k = 0; k < 2; k++)
            {
                C.p[i][j] = (C.p[i][j] + A.p[i][k]*B.p[k][j]%mod)%mod;
            }
        }
    }
    return C;
}

Matrix quick_pow(Matrix A, lint pow)//矩阵快速幂
{
    Matrix I;
    while(pow)
    {
        if(pow & 1)
        {
            I = Mutiple(I, A);
        }
        pow >>= 1;
        A = Mutiple(A, A);
    }
    return I;
}

lint quick_pow(lint base, lint pow)
{
    lint ret = 1;
    while(pow)
    {
        if(pow & 1)
        {
            ret = (ret * base)%mod;
        }
        pow >>= 1;
        base = (base* base)%mod;
    }
    return ret;
}

string s;
lint times;

int main()
{
    cin>>s>>times;
    lint ans = 0;
    for(lint i = 0; i <= s.size() - 1; i++)
    {
        if(s[i] == '0' || s[i] == '5')
        {
            ans = (ans + quick_pow(2, i))%mod;//统计s中可以的可能数
        }
    }
    lint base = quick_pow(2, s.size());
    Matrix I;
    I.p[0][0] = 1;
    I.p[0][1] = 0;
    I.p[1][0] = 1;
    I.p[1][1] = base;
    I = quick_pow(I, times);
    cout<<(ans*I.p[1][0])%mod<<endl;
    return 0;
}

好了,就写到这里吧,,

刚才一个很好的高中同学有些情绪低落,中途去安慰了一下,讨论了一下人生观,希望她能挺过这些困难,走向灿烂的阳光~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值