题目大意
n n n连环的装卸要遵守以下两个规则:
- 一号环(最右边的)在任何时候都可以任意装上或卸下
- 如果 k k k号环没有被卸下,且 k k k号环右边的所有环都被卸下,则 k + 1 k+1 k+1号环可以任意装上或卸下
求将 n n n连环上的环全部取出的最小装卸次数。
有 m m m组数据。
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 10 1\leq n\leq 10^5,1\leq m\leq 10 1≤n≤105,1≤m≤10
题解
前置知识:快速傅里叶变换(FFT)
设
f
n
f_n
fn表示将
n
n
n连环上的环全部取出的最小装卸次数。我们发现,要拆下所有环,首先要将
n
n
n号环拆下,前提是
1
1
1到
n
−
2
n-2
n−2号环都被拆下,然后将
n
n
n号环拆下。接下来拆
n
−
1
n-1
n−1号环,此时
1
1
1到
n
−
2
n-2
n−2号环都已被拆下,要拆
n
−
1
n-1
n−1号环就要先装上
n
−
2
n-2
n−2号环,继续推得要装上
n
−
3
n-3
n−3号环,以此类推,要将
1
1
1到
n
−
2
n-2
n−2号环全部装上,然后就变成了一个
n
−
1
n-1
n−1连环。
重新理一下:
- 拆掉 1 1 1到 n − 2 n-2 n−2号环,装卸次数为 f n − 2 f_{n-2} fn−2
- 拆掉 n n n号环,装卸次数为 1 1 1
- 装上 1 1 1到 n − 2 n-2 n−2号环,装卸次数为 f n − 2 f_{n-2} fn−2
- 拆掉 1 1 1到 n − 1 n-1 n−1号环,装卸次数为 f n − 1 f_{n-1} fn−1
由此可以得到递推式 f n = f n − 1 + 2 f n − 2 + 1 f_n=f_{n-1}+2f_{n-2}+1 fn=fn−1+2fn−2+1。
令 g n = f n + f n − 1 + 1 g_n=f_n+f_{n-1}+1 gn=fn+fn−1+1, g 1 = 2 g_1=2 g1=2,将 f n f_n fn按上述递推式拆开可得 g n = f n + f n − 1 + 1 = 2 ( f n − 1 + f n − 2 + 1 ) = 2 g n − 1 g_n=f_n+f_{n-1}+1=2(f_{n-1}+f_{n-2}+1)=2g_{n-1} gn=fn+fn−1+1=2(fn−1+fn−2+1)=2gn−1,即 g n = 2 n g_n=2^n gn=2n。
再来推 f n f_n fn。由 f n = f n − 1 + 2 f n − 2 + 1 f_n=f_{n-1}+2f_{n-2}+1 fn=fn−1+2fn−2+1可得 f n = f n − 2 + g n − 1 = f n − 2 + 2 n − 1 f_n=f_{n-2}+g_{n-1}=f_{n-2}+2^{n-1} fn=fn−2+gn−1=fn−2+2n−1。
如果 n n n为偶数,令 n = 2 k n=2k n=2k,则 f 2 k = f 2 k − 2 + 2 2 k − 1 = 2 2 k − 1 + 2 2 k − 3 + ⋯ + 2 1 f_{2k}=f_{2k-2}+2^{2k-1}=2^{2k-1}+2^{2k-3}+\cdots+2^1 f2k=f2k−2+22k−1=22k−1+22k−3+⋯+21。
由等比数列可得 f 2 k = 2 2 k + 1 − 2 3 f_{2k}=\dfrac{2^{2k+1}-2}{3} f2k=322k+1−2。
如果 n n n为奇数,令 n = 2 k + 1 n=2k+1 n=2k+1,则 g 2 k + 1 = f 2 k + 1 + f 2 k + 1 g_{2k+1}=f_{2k+1}+f_{2k}+1 g2k+1=f2k+1+f2k+1, f 2 k + 1 = g 2 k + 1 − f 2 k − 1 = 2 2 k + 1 × 3 − 2 2 k + 1 + 2 − 3 3 f_{2k+1}=g_{2k+1}-f_{2k}-1=\dfrac{2^{2k+1}\times 3-2^{2k+1}+2-3}{3} f2k+1=g2k+1−f2k−1=322k+1×3−22k+1+2−3。
整理得 f 2 k + 1 = 2 k + 2 − 1 3 f_{2k+1}=\dfrac{2^{k+2}-1}{3} f2k+1=32k+2−1。
综上所述, f n = ⌊ 2 n + 1 3 ⌋ f_n=\lfloor\dfrac{2^{n+1}}{3}\rfloor fn=⌊32n+1⌋。
我们发现要用高精度,而且一次乘法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),用快速幂的话总时间复杂度为 O ( n 2 log n ) O(n^2\log n) O(n2logn)。考虑用 F F T FFT FFT优化高精度乘法,则总时间复杂度为 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
const double pi=acos(-1.0);
struct big{
int len,a[N+5];
friend big operator/(const big ax,const int bx){
big cx;
cx.len=ax.len;
int now=0;
for(int i=ax.len;i>=0;i--){
cx.a[i]=(now*10+ax.a[i])/3;
now=(now*10+ax.a[i])%3;
}
while(cx.len&&cx.a[cx.len]==0) --cx.len;
return cx;
}
}ans;
struct cp{
double a,b;
friend cp operator+(const cp ax,const cp bx){
return (cp){ax.a+bx.a,ax.b+bx.b};
}
friend cp operator-(const cp ax,const cp bx){
return (cp){ax.a-bx.a,ax.b-bx.b};
}
friend cp operator*(const cp ax,const cp bx){
return (cp){ax.a*bx.a-ax.b*bx.b,ax.a*bx.b+ax.b*bx.a};
}
}w,wn,a1[N+5],a2[N+5];
void ch(cp *a,int l){
for(int i=1,j=l/2,k;i<l-1;i++){
if(i<j) swap(a[i],a[j]);
k=l/2;
while(j>=k){
j-=k;k>>=1;
}
j+=k;
}
}
void fft(cp *a,int l,int fl){
ch(a,l);
for(int i=2;i<=l;i<<=1){
wn=(cp){cos(fl*2*pi/i),sin(fl*2*pi/i)};
for(int j=0;j<l;j+=i){
w=(cp){1,0};
for(int k=j;k<j+i/2;k++,w=w*wn){
cp t=a[k],u=w*a[k+i/2];
a[k]=t+u;
a[k+i/2]=t-u;
}
}
}
}
big tim(const big &ax,const big &bx){
int l1=ax.len+1,l2=bx.len+1;
int l=1;
while(l<l1+l2) l<<=1;
for(int i=0;i<l1;i++) a1[i]=(cp){(double)ax.a[i],0};
for(int i=l1;i<l;i++) a1[i]=(cp){0,0};
for(int i=0;i<l2;i++) a2[i]=(cp){(double)bx.a[i],0};
for(int i=l2;i<l;i++) a2[i]=(cp){0,0};
fft(a1,l,1);fft(a2,l,1);
for(int i=0;i<l;i++) a1[i]=a1[i]*a2[i];
fft(a1,l,-1);
big re;
re.len=ax.len+bx.len;
for(int i=0;i<l1+l2-1;i++) re.a[i]=(int)(a1[i].a/l+0.5);
for(int i=0;i<=re.len;i++){
if(re.a[i]>=10){
if(i==re.len) re.a[++re.len]=0;
re.a[i+1]+=re.a[i]/10;
re.a[i]%=10;
}
}
while(re.len&&re.a[re.len]==0) --re.len;
return re;
}
big gt(int t){
big re;
re.a[re.len=0]=t;
return re;
}
big mi(big bs,int t){
big re=gt(1);
while(t){
if(t&1) re=tim(re,bs);
t>>=1;bs=tim(bs,bs);
}
return re;
}
void print(){
for(int i=ans.len;i>=0;i--){
printf("%d",ans.a[i]);
}
printf("\n");
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
ans=mi(gt(2),n+1)/3;
print();
}
return 0;
}
文章介绍了如何通过快速傅里叶变换(FFT)优化算法来解决九连环问题中的最小装卸次数计算问题,利用递推关系推导出最终的解法,降低了时间复杂度至O(nlog^2n)。
236

被折叠的 条评论
为什么被折叠?



