數羊
H題數羊
第八屆“圖靈杯”NEUQ-ACM程式設計競賽個人賽
題目描述
憨憨小楊晚上睡不著覺,就開始數羊,她覺得一隻一隻數太慢了,突發奇想出了一種新的數羊方式,羊羊數量A(n,m)由兩個整形變數n和m決定,計算方式如下:
現在給出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)=2∗n
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相乘,因此
這樣就根據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;
else
return 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;
}