[atARC135F]Delete 1,4,7,...

记$n_{k}$表示经过$k$轮操作后的元素个数,显然$n_{k}=\lfloor\frac{2n_{k-1}}{3}\rfloor$(初始$n_{0}=n$)

记$f^{k}(i)$表示$k$轮操作后的第$i$个元素在操作前的位置,显然$f^{k}(i)=\lceil\frac{3f^{k-1}(i)}{2}\rceil$

问题即求$\sum_{i=1}^{n_{k}}f^{k}(i)$,暴力计算时间复杂度为$o(nk\cdot (\frac{2}{3})^{k})$

另一方面,注意到$f^{k}(i+2^{k})=f^{k}(i)+3^{k}$,具体可以归纳证明

进一步的,折半取$x+y=k$,显然$f^{k}(i)=f^{x}(f^{y}(i))$​,代入原式即求
$$
\sum_{i=1}^{n_{k}}f^{k}(i)=\sum_{i=1}^{2^{y}}\sum_{i+j\cdot 2^{y}\le n_{k}}f^{x}(f^{y}(i)+j\cdot 3^{y})
$$
关于后者,将$\lfloor\frac{n_{k}-i}{2^{y}}\rfloor$拆成二进制表示,进而问题即求形如$\sum_{j=0}^{2^{t}-1}f^{x}(a+j\cdot 3^{y})$

记上式为$F_{t}(a)$,转移即
$$
F_{t}(a)=\begin{cases}F_{t-1}(a)+F_{t-1}(a+2^{t-1}3^{y})&t\ge 1\\f^{x}(a)&t=0\\F_{t}(a-s\cdot 2^{x})+s\cdot 2^{t}3^{x}&s=\lfloor\frac{a}{2^{x}}\rfloor\ge 1\end{cases}
$$
状态仅有$o(2^{x}\log n)$个,预处理出来即可,时间复杂度为$o(2^{\frac{k}{2}}(k+\log n))$($k$是计算$f^{y}(i)$和$f^{x}(a)$)

结合上述两个做法,按$k\le 40$和$k>40$分类即可通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define K0 40
 4 #define N 50
 5 #define K 20
 6 #define mod 998244353
 7 #define ll long long
 8 #define LL __int128
 9 int k,x,y,ans,F[N][1<<K];
10 ll n,mi[K+5];
11 ll calc(int k,ll x){
12     for(int i=0;i<k;i++)x=(3*x+1>>1);
13     return x;
14 }
15 int get_F(int t,LL a){
16     if (a>=(1<<x)){
17         LL s=(a>>x);
18         return (get_F(t,a-(s<<x))+(s%mod<<t)*mi[x])%mod;
19     }
20     if (F[t][a]>=0)return F[t][a];
21     if (!t)return F[t][a]=calc(x,a)%mod;
22     return F[t][a]=(get_F(t-1,a)+get_F(t-1,a+((LL)mi[y]<<t-1)))%mod;
23 }
24 int main(){
25     mi[0]=1;
26     for(int i=1;i<K+5;i++)mi[i]=3*mi[i-1];
27     scanf("%lld%d",&n,&k);
28     for(int i=0;i<k;i++)n=(n<<1)/3;
29     if (k<=K0){
30         x=(k>>1),y=k-x;
31         memset(F,-1,sizeof(F));
32         for(int i=1;i<=(1<<y);i++){
33             LL n0=(n-i>>y)+1,a=calc(y,i);
34             for(int t=0;t<N;t++)
35                 if ((n0>>t)&1){
36                     ans=(ans+get_F(t,a))%mod;
37                     a+=((LL)mi[y]<<t);
38                 }
39         }
40         printf("%d\n",ans);
41         return 0;
42     }
43     if (k>K0){
44         for(int i=1;i<=n;i++)ans=(ans+calc(k,i))%mod;
45         printf("%d\n",ans);
46         return 0;
47     }
48 }
View Code

 

posted @ 2022-02-24 15:58  PYWBKTDA  阅读(114)  评论(0编辑  收藏  举报