ZJNU 1374 - 情书抄写员——高级 (矩阵快速幂)

ZJNU 1374 - 情书抄写员——高级

题面

Wind的女友数量是惊人的。每个月开始时,Wind的每一个正式女友都会给他介绍k个新的女生,我们称这样的新人为“潜在的女友”(Potential GirlFriend)。通过近两个月的交往,潜在的女友总会在下一个月末成为正式的女友,并在第三个月初开始每月介绍新的女友。

我们假设,在第一个月Wind只有一个潜在的女友。Wind每个月都要给他的所有女友和潜在女友(包括本月初刚介绍来的人)写一封情书。在第a个月和第b个月,Wind有事不在,需要雇用一些“情书抄写员”来代替他完成这个操作。Wind将会让每个情书抄写员负责t封情书,并希望这个t值可以使得第a个月和第b个月的任务都能正好分尽。
Wind拜托“实习生”佳佳计算出第a个月和第b个月各需要写多少情书。为了雇用尽可能少的抄写员,Wind还想知道t的最大值是多少。

由于答案可能非常大,因此你只需要输出这三个数值mod m的结果即可。

思路

手模小样例,将潜在女友的第一个月和第二个月分为两行,正式女友分为一行,根据题意得出正式女友和潜在女友第一个月的数量相同,潜在女友第二个月滞后一项,女友总和超前一项,呈斐波那契数列方式分布

得出题意就是求\(Fibo(a)\)\(Fibo[b]\)\(\gcd(Fibo[a],Fibo[b])\)三项

由斐波那契性质\(\gcd(F_a,F_b)=F_{\gcd(a,b)}\)得出第三项可以看作\(Fibo_{\gcd(a,b)}\)

于是题目就是通过矩阵快速幂快速求出斐波那契上述三项的值

#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define all(a) (a).begin(),(a).end()
#define SUM(a) accumulate(all(a),0LL)
#define MIN(a) (*min_element(all(a)))
#define MAX(a) (*max_element(all(a)))
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
ll mod=998244353;
const int dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
void debug(){cerr<<'\n';}template<typename T,typename... Args>void debug(T x,Args... args){cerr<<"[ "<<x<< " ] , ";debug(args...);}
mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

struct quickpow_fibonacci
{
    struct matrix
    {
        ll n,m,data[2][2];
        void init()
        {
            for(int i=0;i<n;i++)
                data[i][i]=1;
        }
    };
    matrix multi(matrix &a,matrix &b)
    {
        matrix t;
        t.n=a.n;
        t.m=b.m;
        for(int i=0;i<t.n;i++)
        {
            for(int j=0;j<t.m;j++)
                t.data[i][j]=0;
        }
        for(int i=0;i<a.n;i++)
        {
            for(int k=0;k<a.m;k++)
            {
                if(a.data[i][k]>0)
                {
                    for(int j=0;j<b.m;j++)
                        t.data[i][j]=(t.data[i][j]+qmul(a.data[i][k],b.data[k][j]))%mod;
                }
            }
        }
        return t;
    }
    matrix fast_mod(matrix &a,ll n)
    {
        matrix r;
        r.n=a.n;
        r.m=a.m;
        memset(r.data,0,sizeof(r.data));
        r.init();
        while(n>0)
        {
            if(n&1)
                r=multi(r,a);
            a=multi(a,a);
            n>>=1;
        }
        return r;
    }
    ll solve(ll n,ll k)
    {
        matrix a,b;
        a.n=1;
        a.m=2;
        a.data[0][0]=1;
        a.data[0][1]=1;
        b.n=2;
        b.m=2;
        b.data[0][0]=0;
        b.data[0][1]=k;
        b.data[1][0]=1;
        b.data[1][1]=1;
        b=fast_mod(b,n-1);
        return multi(a,b).data[0][0];
    }
}f;

void solve()
{
    ll k,a,b;
    cin>>k>>mod>>a>>b;
    cout<<f.solve(a,k)<<'\n';
    cout<<f.solve(b,k)<<'\n';
    cout<<f.solve(gcd(a,b),k)<<'\n';
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值