T1.Crazy

题意:求$\sum\limits_{i=1}^{n}{Fib[i]^2}$其中Fib[i]表示斐波那契数列第i项,Fib[1]=Fib[2]=1;(n<=1e15)
分析:通过猜想与验证,我们发现$\sum\limits_{i=1}^{n}{Fib[i]^2}=Fib[n]\times Fib[n+1]$
证明:显然这个结论对于n=1是成立的。我们假设这个结论是正确的,那么有
$$
\sum\limits_{i=1}^{n}{Fib[i]^2}=\sum\limits_{i=1}^{n-1}{Fib[i]^2}+Fib[n]^2=Fib[n-1]Fib[n]+Fib[n]^2=Fib[n]Fib[n+1]
$$
然后我们可以用矩阵快速幂来求Fib[i]

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

#define MD 1000000007

typedef long long ll;

typedef struct _matrix
{
    ll x[3][3];
}matrix;

inline matrix mul(matrix a,matrix b)
{
    matrix c;
    c.x[1][1]=((a.x[1][1]*b.x[1][1])%MD+(a.x[2][1]*b.x[1][2]%MD))%MD;
    c.x[1][2]=((a.x[1][2]*b.x[1][1])%MD+(a.x[2][2]*b.x[1][2]%MD))%MD;
    c.x[2][1]=((a.x[1][1]*b.x[2][1])%MD+(a.x[2][1]*b.x[2][2]%MD))%MD;
    c.x[2][2]=((a.x[1][2]*b.x[2][1])%MD+(a.x[2][2]*b.x[2][2]%MD))%MD;
    return c;
}

inline matrix setmat(ll a,ll b,ll c,ll d)
{
    matrix mat;
    mat.x[1][1]=a,mat.x[1][2]=b,mat.x[2][1]=c,mat.x[2][2]=d;
    return mat;
}

inline matrix ksm(matrix a,ll t)
{
    matrix ans;
    ans=setmat(1,0,0,1);
    while(t)
    {
        if(t&1)ans=mul(a,ans);
        a=mul(a,a);
        t>>=1;
    }
    return ans;
}

inline ll getfib(ll num)
{
    if(num==1||num==2)return 1;
    matrix but;
    but=setmat(1,1,1,0);
    but=ksm(but,num-2);
    return ((but.x[1][1]+but.x[1][2])%MD);
}

int main()
{
    freopen("crazy.in","r",stdin);
    freopen("crazy.out","w",stdout);
    ll n;
    scanf("%lld",&n);
    cout<<(getfib(n)*getfib(n+1))%MD<<endl;
    return 0;
}

T2.lucky
题意:对于区间[L,R]内求有多少个数是LN的倍数且不是任何一个BN的倍数(BN个数<=15,1<=L,R<=1e15)
分析:容斥搞一搞,但要注意$\prod BN$越界后直接返回

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

ll L,R,ln,n,un[20];
ll ans;

void input()
{
    scanf("%lld%lld%lld%lld",&ln,&n,&L,&R);
    for(ll i=1;i<=n;i++)scanf("%lld",&un[i]);
}

ll gcd(ll a,ll b)
{
    return (b==0?a:gcd(b,a%b));
}

void sch(ll now,ll pos,ll num)
{
    if(now<=0)return;
    if(pos==n+1)
    {
        if(num%2==1)ans+=((R/now)-((L-1)/now));
        else ans-=((R/now)-((L-1)/now));
        return ;
    }
    sch(now/gcd(now,un[pos])*un[pos],pos+1,num+1);
    sch(now,pos+1,num);
}

int main()
{
    freopen("lucky.in","r",stdin);
    freopen("lucky.out","w",stdout);
    input();
    sch(ln,1,1);
    cout<<ans<<endl;
    return 0;
}


T3.feed
题意:用两个给定大小的勺子把无限多的粥转移到无限大的碗里或转移出去,每次勺子必须装满。求碗里能否有X单位的粥。
分析:扩展欧几里得。
我们需要知道对于ax+by=c的通解表达式,并且将表达式表示成坐标系中的直线,求两条直线纵坐标绝对值的和的最小值。

#include <iostream>
#include <cstring>
#include <cstdio>

#define mabs(a) ((a)>0?(a):-(a))

using namespace std;

typedef long long ll;

ll exgcd(ll x,ll y,ll &a,ll &b)
{
    if(y==0)
    {
        a=1;
        b=0;
        return x;
    }
    ll ret,tmp;
    ret=exgcd(y,x%y,a,b);
    a=a-b*(x/y);
    swap(a,b);
    return ret;
}

ll sa,sb,targ;

void work()
{
    ll a,b,g,a0,b0,da,db;
    g=exgcd(sa,sb,a0,b0);
    if(targ%g){cout<<"BeiJu!"<<endl;return;}
    a0*=targ/g;
    b0*=targ/g;

    da=mabs(sb/g);
    db=-mabs(sa/g);

    ll dt1,dt2;
    dt1=-(a0/da);
    dt2=-(b0/db);

    ll mans=9999999999;

    for(ll i=dt1-1;i<=dt1+1;i++)mans=min(mans,mabs(db*i+b0)+mabs(da*i+a0));
    for(ll i=dt2-1;i<=dt2+1;i++)mans=min(mans,mabs(db*i+b0)+mabs(da*i+a0));

    cout<<mans<<endl;
}

int main()
{
    freopen("feed.in","r",stdin);
    freopen("feed.out","w",stdout);
    cin>>sa>>sb;
    while(cin>>targ)work();
    return 0;
}


分享至ヾ(≧∇≦*)ゝ:
分类: 所有

发表评论

电子邮件地址不会被公开。 必填项已用*标注

你是机器人吗? =。= *