1. 程式人生 > 其它 >數羊

數羊

技術標籤:Story的故事程式設計c++數學

H題數羊

第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽

題目描述

憨憨小楊晚上睡不著覺,就開始數羊,她覺得一隻一隻數太慢了,突發奇想出了一種新的數羊方式,羊羊數量A(n,m)由兩個整形變數n和m決定,計算方式如下:
img
現在給出n和m的值,請你幫小楊數數一共有多少隻羊。

輸入描述:

多組輸入。第一行包含一個整數T(1≤T≤1000),表示有T組測試資料。每組測試資料包含一行,包含兩個整數n(1≤n≤10^9)和m(0≤m≤2).

輸出描述:

對每一組輸入,在一行中輸出A(n,m)的值,由於輸出的結果可能會很大,答案對998244353取模

示例1

輸入

3
3 0
3 1
3 2

輸出

5
6
8

當初看到這題的時候,這不就是一個分段函式嗎,只是用到了遞迴,可當我寫完之後並提交,發現爆記憶體了。

原來,像這種 阿克曼函式 ,最後一個函式遞迴太多次,把棧遞迴穿了,那該怎麼辦呢?

有沒有發現,題目的資料範圍中,0≤m≤2,所以m只能是0,1,2。所以我們可以分類討論。

1.當m==0的時候,上面已經有對應的表示式了。

2.當m == 1的時候,則A(n,1)=A(A(n-1,1),0),這時就可以套用第三個函式,也就是說,A(n,1)=A(n-1,1)+2。左邊的n一直減小,直到n==0,就到了第二個式子,等於1,這裡面加了n-1個2和最後一個1,所以,A(n,1)=2*(n-1)+1嗎?其實不是的。

我們觀察最後一個函式,發現裡面有個n-1,也就是說當n == 1的時候A(1,m)=A(A(0,m),m-1),由第二個式子A(0,m)=1得,A(1,m)=A(1,m-1),m一直減小,直到m==0,此時符合第一個式子,等於2,因此我們得出一個結論:
A ( 1 , m ) = 2 A(1,m)=2 A(1,m)=2

回到剛才,當n減小到1的時候就停在了2,所以實際上有n個2相加,所以
A ( n , 1 ) = 2 ∗ n A(n,1)=2*n A(n,1)=2n

3.當m==2的時候,也用同樣的分析方法,A(n,2)=A(A(n-1,2),1),此時套用上面的結論A(n,1)=2*n,得出A(n,2)=A(A(n-1,2),1)=2A(n-1,2),當n減小到1時符合第一個式子,有n個2相乘,因此

A ( n , 2 ) = p o w ( 2 , n ) A(n,2)=pow(2,n) A(n,2)=pow(2,n)

這樣就根據m的取值,進行了分類討論,將看似是阿克曼函式的問題轉化成了單純用if語句就能做的問題

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#define endl '\n'
#define int long long
#define Please return
#define AC 0
using namespace std;
const int N=1e5+7;
void fastio(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);}
int qpow(int a,int b,int m){int ans=1;while(b){if(b&1)ans=ans*a%m;b>>=1;a=a*a%m;}return ans;}

int func(int a,int b)
{
  if(b==0)return (a+2)%998244353;
  else if(b==1)return (2*a)%998244353;
  elsereturn qpow(2,a,998244353);//快速冪
}

signed main()
{
  fastio();
  int t;
  cin>>t;
  while(t--)
  {int a,b;
​    cin>>a>>b;
​    cout<<func(a,b)<<endl;
  }
  Please AC;
}