看了好久题解的说
设 \(f^i(x)\) 为经历了 \(i\) 轮之后的 \(x\) 的为位置对于的初始下标。
那么有 \(f(x) = \lfloor\frac{3x+1}{2}\rfloor\)
\(f^i(x) = f(f(f()....)\)
考虑对这个柿子进行性质的观察。
我们最后只会剩余 \(
frac{2n}{3}\) 个数,如果暴力则是 \(O(k(\frac{2}{3})^kn)\)
当 \(k\) 大时这个复杂度可以接受。
但是 \(k\) 小时,我们需要另辟蹊径。
我们发现 \(f^k(x + i2^k) = f^k(x) + i3^k\)
考虑设\(l + r = k\)
考虑折半跑路。
$f^k(x + i 2^r)= fl(fr(x) + i * 3^r) $
考虑\(g(a,j) = \sum^{2^j - 1}_{i = 0}f^l(a + i3^x)\)
我们考虑直接递推其,最后可以通过倍增和递归来处理出。
点击查看代码
#include <cstdio>
#include <unordered_map>
#pragma GCC optimize(2,3,"Ofast")
using namespace std;
typedef long long ll;
const int P=998244353;
ll n,res;int k;
ll f(ll x,int i){
for(;i;--i) x=(3*x+1)/2;
return x;
}
ll o[103],w[103],msk;
int L,R;
unordered_map<ll,int> mp[103];
int g(ll x,int i){
if(x-1>msk) return (g(((x-1)&msk)+1,i)+(((x-1)>>L)%P)*(w[L]%P)%P*((1ll<<i)%P)%P)%P; //对内层的 a 进行 2^l 的平移
if(!i) return mp[i][x]=f(x,L)%P;
if(mp[i].find(x)!=mp[i].end()) return mp[i][x];
return mp[i][x]=(g(x,i-1)+g(x+w[R]*(1ll<<(i-1)),i-1))%P;
}
int main(){
scanf("%lld%d",&n,&k);
o[0]=n;
for(int i=1;i<=k;++i) o[i]=o[i-1]*2/3;
if(k>40){//是的……需要数据分治……毒瘤
int res=0;
for(int i=1;i<=o[k];++i)
res=(res+f(i,k))%P;
printf("%d\n",res);
return 0;
}
L=k/2;R=k-L;w[0]=1;msk=(1ll<<L)-1;
for(int i=1;i<=30;++i) w[i]=w[i-1]*3;
for(int i=1;i<=o[k]&&i<=(1ll<<R);++i){
ll c=f(i,R),upb=((o[k]-i)>>R)+1;
for(int j=50;~j;--j)
if(upb>>j&1){
res=(res+g(c,j))%P;
c+=w[R]<<j; //对外层的 a 进行 2^r 的平移,拼接成一段前缀
}
}
printf("%lld\n",res);
return 0;
}