2023“钉耙编程”中国大学生算法设计超级联赛(2)Card Game
题目大意
有 n n n个牌堆,第一个牌堆有 k k k张牌,从下到上的点数分别为 ( k , k − 1 , k − 2 , … , 2 , 1 ) (k,k-1,k-2,\dots,2,1) (k,k−1,k−2,…,2,1)。其余牌堆都是空堆。
你可以将一个牌堆顶部的牌移到另一个牌堆的顶部。在任意时刻,每堆牌从底部到顶部都要满足值递减且连续。
你的任务是要将所有牌从第一个牌堆移到第二个牌堆。
在已知 n n n的情况下,求使得你可以完成任务的 k k k的最大值。
输出答案模 998244353 998244353 998244353后的值。
有 T T T组数据。
1 ≤ T ≤ 1 0 5 , 2 ≤ n ≤ 1 0 9 1\leq T\leq 10^5,2\leq n\leq 10^9 1≤T≤105,2≤n≤109
题解
因为牌堆要求值递减且连续,所以最大的一张牌一定是从牌堆 1 1 1直接移到牌堆 2 2 2。在移动这张牌之前,我们要将其他牌从牌堆 1 1 1移到空牌堆,在移动完最大的牌之后在用类似的方法移回。移出起点牌堆和移入终点牌堆的过程是对称的,所以我们可以将问题转化为:有一个空的终点牌堆和若干个有牌的牌堆,你要将所有的牌移到终点牌堆。
设 f i f_i fi表示有 i i i个牌堆时的答案,显然 f 2 = 1 f_2=1 f2=1。当 n > 2 n>2 n>2时,我们先移动含有点数最大的牌的牌堆,此时除了终点牌堆以外的牌堆的牌都有牌且牌的点数都小于这个牌堆的任意一张牌,所以这些牌堆不能使用,这一步可以移动 f 2 f_2 f2张牌。然后我们移动剩下的牌堆中含有点数最大的牌的牌堆,由于上一步的操作,我们得到了一个新牌堆,于是在一部我们可以移动 f 3 f_3 f3张牌。以此类推,可得 f n = ∑ i = 2 n − 1 f ( i ) f_n=\sum\limits_{i=2}^{n-1}f(i) fn=i=2∑n−1f(i),由递推式可得 f n = 2 n − 1 f_n=2^n-1 fn=2n−1,由此即可得到答案。
时间复杂度为 O ( T log n ) O(T\log n) O(Tlogn)。
code
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
int t;
long long n,ans;
long long mi(long long t,long long v){
if(!v) return 1;
long long re=mi(t,v/2);
re=re*re%mod;
if(v&1) re=re*t%mod;
return re;
}
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d",&n);
ans=(mi(2,n-1)-1+mod)%mod;
printf("%lld\n",ans);
}
return 0;
}