链接:http://codeforces.com/problemset/problem/327/C
题意:给一串数,这串数重复k次,求去掉一些数后得到的新数能被5整除,问有多少种方法得到满足条件的数。
思路:要求能被5整除,则数的末位必然是0或5,求出原始串中0和5的位置,从0开始计数,位置为i1,i2,i3,,,in,则有a=2^i1+2^i2+...+2^in种方法。则在所有中的方法总数为:s=a*(q^n-1)/(q-1),q=2^len,len为原始串的长度。这个公式推一下就出来了。由于数据范围很大,所以要模1e9+7。在求幂的时候采用的是快速幂来做。除以(q-1)的话,求出它的逆元。由欧拉定理,(a,n)=1,a^phi(n)=1(mod n),a的逆为a^(phi(n)-1),n为素数时,phi(n)=n-1,所以a的逆为a^(n-2)%n.
#include<cstdio> #include<cstring> using namespace std; typedef long long LL; char a[100002]; int k; int mod=1e9+7; //LL fastmi(LL a, LL n) //{ // if (n == 0) // return 1; // else if (n % 2 == 1) // return (a * fastmi(a, n - 1)) % mod; // else // { // LL temp = fastmi(a, n / 2); // return (temp * temp) % mod; // } //} LL fastmi(LL a, LL b) { LL ans=1; while (b) { if (b&1){ans = (ans*a)%mod; b--;} b/=2; a = a * a % mod; } return ans; }//由78s降到了46s
int main() { scanf("%s",a); scanf("%d",&k); int len=strlen(a); LL ans=0; for(int i=0; i<len; i++) if(a[i]=='0' || a[i]=='5') { ans=(ans+fastmi(2,i))%mod; } LL q=fastmi(2,len); LL p=fastmi(q,k)%mod; p=(p-1+mod)%mod; LL q1=(q-1+mod)%mod; ans=(ans*p)%mod; q1=fastmi(q1,mod-2)%mod;//欧拉定理 ans=(ans*q1)%mod; printf("%I64d\n",ans); return 0; }
一开始不知道欧拉定理,公式也没化简,用循环直接求,自然T了。公式的话,能化简就化简,化简了可以看出一些东西来。
究竟是我抛弃了历史,还是历史遗弃了我。