[fzu]P2177 特殊的数 (imp)手推公式+快速幂

昨晚睡觉躺床上突然YY出来这么个做法……今天试验了下还真可行,虽然和设想有些出入……
VC++的空间占用比GNU小,时间嘛因为一个不严谨的错误结果进了15ms,至于代码长度……还算比较短吧,如果不粘思路部分

直接粘代码……

/*
  用s[i=1..10^18,0..1,0..1]表示当前有i位,0/1表示有偶数/奇数个7,另一对0/1表示9
    s[i,0,0]=s[i-1,0,0]*3+s[i-1,0,1]+s[i-1,1,0] (1)
    s[i,1,1]=s[i-1,1,1]*3+s[i-1,0,1]+s[i-1,1,0] (2)
    s[i,1,0]=s[i-1,1,0]*3+s[i-1,0,0]+s[i-1,1,0] (3)
    s[i,0,1]=s[i-1,0,1]*3+s[i-1,0,0]+s[i-1,1,1] (4)
    初值的话s[1,0,1]=s[1,1,0]=1; s[1,0,0]=3; s[1,1,1]=0
  首先(1)+(2)/(3)+(4)得到按79奇偶性相同/相异划分的两个递推式
    s[i,0,0]+s[i,1,1]=(s[i-1,0,0]+s[i-1,1,1])*3+(s[i-1,1,0]+s[i-1,0,1])*2
    s[i,1,0]+s[i,0,1]=(s[i-1,1,0]+s[i-1,0,1])*3+(s[i-1,0,0]+s[i-1,1,1])*2
  用a[i,0]表示79奇偶性相同的情况,a[i,1]表示不同的
    a[i,0]=a[i-1,0]*3+a[i-1,1]*2
    a[i,1]=a[i-1,1]*3+a[i-1,0]*2
    初值a[1,0]=3; a[1,1]=2
  相加可以得到总情况数5^n不过这是废话一眼就能看出来,做差
    a[i,0]-a[i,1]=a[i-1,0]*(3-2)+a[i-1,1]*(2-3)=a[i-1,0]-a[i-1,1]
    发现79奇偶性相同和不同情况之差总是定值,也就是1
    这样直线得到了a[n,0]=s[n,0,0]+s[n,1,1]=(5^n+1)/2 (5)
  再用a[n,0]回推s[n,0,0],考虑(1)-(2)
    s[i,0,0]-s[i,1,1]=3*(s[i-1,0,0]-s[i-1,1,1])
    等比数列,那么s[n,0,0]-s[n,1,1]=3^n (6)
  (5)+(6)就得到了
    2ans=(5^n+1)/2+3^n
  套公式的时候注意下奇偶性就可以了,最后加起来还要再模一下 
  应该有排列组合一步到位的思路吧?
 */
#include <cstdio>
using namespace std;
#define ll long long
#define MOD 1000000007

int T; ll n;
ll shr(ll x) {if (x&1) return ((x+MOD)>>1); return (x>>1);}
ll qmod(ll a, ll b) {
  ll t=1; 
  for (; b; b>>=1,a=a*a%MOD) if (b&1) t=t*a%MOD;
  return t;
}
int main() {
  int T; scanf("%d",&T);
  while (T--) {
    scanf("%I64d",&n);
    printf("%I64d\n",(shr(qmod(3,n)+shr(qmod(5,n)+1)))%MOD);
  }
  return 0;
}


嗯……至于为啥要新写一段呢,纯粹是为了凑每月4篇……我太懒了……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值