2 条题解

  • 2
    @ 2023-12-30 22:57:31

    提供一个不超纲的O(logn)O(\log n)做法。

    构造方程a2=a+1a^{2}=a+1,则有:

    a3=a2+a=2a+1a^{3}=a^{2}+a=2a+1

    a4=a3+a2=3a+2a^{4}=a^{3}+a^{2}=3a+2

    a5=a4+a3=5a+3a^{5}=a^{4}+a^{3}=5a+3

    a6=a5+a4=8a+5a^{6}=a^{5}+a^{4}=8a+5

    ……

    an=f(n)a+f(n1)a^{n}=f(n)a+f(n-1)(这部分想要严谨证明可以使用数学归纳法)

    所以我们只需要构造an=pa+qa^{n}=pa+q,即可求出f(n)=pf(n)=p

    ana^{n}的性质,显然有:

    $a^{2n}=(a^{n})^{2}=(f(n)a+f(n-1))^{2}=f(n)^{2}a^{2}+2f(n)f(n-1)a+f(n-1)^{2}=(f(n)^{2}+2f(n)f(n-1))a+f(n)^{2}+f(n-1)^{2}$

    因此我们可以由ana^{n}O(1)O(1)求出a2na^{2n}(以及a2n+1a^{2n+1})。

    所以我们便能使用快速幂递归求解,时间复杂度O(logn)O(\log n)

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n, p;
    struct node
    {
    	int a, b;
    };
    node operator*(node x, node y)
    {
    	int p1 = x.a * y.a, p2 = x.a * y.b + x.b * y.a, p3 = x.b * y.b;
    	return node{(p2 + p1) % p, (p3 + p1) % p};
    }
    node f(int x)
    {
    	if (x == 1)
    	{
    		return node{1, 0};
    	}
    	node c = f(x >> 1);
    	if (x & 1)
    	{
    		return c * c * f(1);
    	}
    	return c * c;
    }
    signed main()
    {
    	cin >> n >> p;
    	cout << f(n).a << endl;
    }
    

    快速幂和多项式乘法应该不算超纲吧。
    赛后发现很多人都用的矩阵快速幂,但是我不会呀。

    • 2
      @ 2023-12-29 16:05:54

      88个点没什么好说的。

      对于n109n\leq 10^9,我们只需要一个小常数做法,此处注意到取模运算只是在加法后进行,所以可以直接先相加,如果加后答案大于等于PP,减去PP即可,比取模快多了。

      对于P1000P\leq 1000,我们记录相邻两项的fi,fi+1f_i,f_{i+1},显然最多模拟P2P^2项以后就会进入周期,我们记录周期的大小即可快速计算。

      对于n1012n\leq 10^{12},我们可以先行模拟,比如f3=f1+f2f_3=f_1+f_2f4=f3+f2=f1+2f2f_4=f_3+f_2=f_1+2f_2f5=f4+f3=2f1+3f2f_5=f_4+f_3=2f_1+3f_2,看看f[1000000]=?f[1]+?f[2]f[1000000]=?f[1]+?f[2],把这两个问号求出来。 那么显然,对于任意的ii,都有f[i]=?f[i999999]+?f[i999998]f[i]=?f[i-999999]+?f[i-999998]

      就可以每次O(1)O(1)的得到10610^6以后的项了,后续细节不表。

      #include<bits/stdc++.h>
      #define ll long long
      #define maxn 1000005
      using namespace std;
      ll n,P;
      ll f1[maxn],f2[maxn];
      int main()
      {
      	cin>>n>>P;
      	f1[1]=1; f2[2]=1;
      	for (int i=3;i<=1000000;i++)
      	{
      		f1[i]=(f1[i-1]+f1[i-2])%P;
      		f2[i]=(f2[i-1]+f2[i-2])%P;
      	}
      	ll now=1,x1=1,x2=1;
      	while (now+999998<=n)
      	{
      		ll n1,n2;
      		n1=(f1[999999]*x1+f2[999999]*x2)%P;
      		n2=(f1[1000000]*x1+f2[1000000]*x2)%P;
      		x1=n1; x2=n2; now+=999998; 
      	}
      	while (now<n)
      	{
      		ll y=(x1+x2)%P;
      		x1=x2; x2=y; now++;
      	}
      	cout<<x1<<endl;
      }
      

      此外看到了赛时除了超纲做法《矩阵快速幂》以外的其他做法,欢迎在题解区发表。

      • 1

      信息

      ID
      1381
      时间
      1500ms
      内存
      512MiB
      难度
      9
      标签
      递交数
      292
      已通过
      17
      上传者