I-Paperfolding(2020 Multi-University Training Contest 5)(数学+找规律)
Time limit:1000 ms
Memory limit:131072 kB
judge:航电OJ
VJudge
Description
There is a piece of paper in rectangular shape with sufficient length and width (lay flat on the table). Execute an operation instruction according to a string of length n from left to right that only contains 4 different characters of L,R,U,D.
-
L instruction means to fold it from left to right,
-
R instruction means to fold from right to left,
-
U instruction means to fold from top to bottom,
-
D instruction means to fold in half from bottom to top.
Note that the operation is limited due to the limitation of the desktop. Namely, the fold operation is restricted. For example, if you fold the paper from left to right, you should let the left side overlap on the right side with no rotation.
Now, cut a knife horizontally (completely cut) at the center of the visible part of the paper, and then cut vertically (completely cut).
The number of pieces of the whole paper split is num(S).
See the example and the picture for better understanding.
Now given a nonnegative integer n, the string S is generated from 4n different possible outcomes in equal probability. Find the expected value of the number of pieces of the paper which is split, that is E(num(S)) mod 998244353.
It can be shown that the answers can be represented by PQ, where P and Q are coprime integers, and print the value of P×Q−1 mod 998244353.
Input
The first line contains a single integer T (1≤T≤105), the number of testcases.
Each of the next T lines contains a number n ( 0≤n≤1018 ).
Output
For each testcase, print the answer in one line.
Sample Input
2
0
1
Sample Output
4
6
题意
给你一张纸,无需考虑纸的长和宽。
你需要把纸按照“上”、“下”、“左”、“右”的任意排列折叠n次。
折叠完之后拿一把刀从纸的中心分别沿横竖切开,此时的纸将被分成若干片。
题目给出折叠的次数,让你求出按照概率相同的不同的折叠方式,得出的数量的数学期望。
结果将是一个分数,求出期望值对998244353取模下的逆元。
题解
直接暴力枚举所有的“上”、“下”、“左”、“右”排列,分别对每一个排列进行模拟求解,排列数为 2 n 2^n 2n,时间复杂度为 O ( 4 n × n ) O(4^n\times n) O(4n×n),n的取值范围是 [ 0 , 1 0 18 ] [0,10^{18}] [0,1018],肯定会超时。
纸片按照不同的折叠方式最后裁剪出的纸片数量是不同的,不过可以看出纸片沿着“上”和“下”折叠的效果是一样的,不影响最后得出的结果、同理向“左”和“右”也是一样的。所以我们在此把“上”和“下”统一归为“上”;把“左”和“右”统一归为“左”。
那么问题就变成了求出“上”和“左”的不同安排得出的纸片数量的数学期望。容易看出,不同的排列数有 2 n 2^n 2n个。
最初的纸片再没有经过任何折叠的时候会裁出4张纸片,由于我们是“上下”方向折叠,所以纸片在“左右”方向上的两部分数量一样。我们将其看作是一对(1,1),加起来是2,即左右两边分别有2片;当向“上”方向折叠一次后会裁出6张纸片,其中左边的上下两部分一部分是1张,一部分是2张,右边也一样,我们同样将其看作是一对(1,2),加起来是3,即左右两边分别有3片;当向“上”方向折叠两次后会裁出10张纸片,同样可以被看做是一对(2,3),加起来是5,即左右两边分别有5片,由此得出沿某一方向折叠k次后将会得到一对 2 k + 1 2^k+1 2k+1个纸片。向“左右”方向同理。
那么同时有“上”和“左”呢?
可以发现当向同一方向反复折叠时得出的数量分布是一对相同的数字,而交叉折叠后两对数字不再相同,因为不同方向的折叠造成的数量增加的方向不同。而“左右”方向折叠时,“上下”方向的两个数字作为了一个整体,增加后其内部比例不会变,例如最初的纸向“上下”折叠一次后数量分布为一对(1,2),即(1,2,1,2),然后再向“左右”折叠一次后数量分布变成了(1,2,2,4)。两对(1,2)中的一对变成了(2,4),但是(1,2)和(2,4)的比例不会变。所以“上下”和“左右”方向叠加后其效果会乘积。也就是有有 i i i个“上”和 n − i n-i n−i个“左”时将会得到 ( 2 i + 1 ) ( 2 n − i + 1 ) (2^i+1)(2^{n-i}+1) (2i+1)(2n−i+1)。且数量只与“上”和“左”的数量有关,和顺序无关。
假设当前有 i i i个“上”和 n − i n-i n−i个“左”,那么可以排列出 C n i C_n^i Cni种情况。而且由于“上”和“左”的数量被固定了,所以裁剪出的纸片数量也就相同。那么这个 i i i产生的贡献就是 C n i ( 2 i + 1 ) ( 2 n − i + 1 ) C_n^i(2^i+1)(2^{n-i}+1) Cni(2i+1)(2n−i+1)。对每一个i求和即得到所有情况的数量和。将数量和除以情况数就是答案,所以答案就是:
1 2 n ∑ i = 0 k C n i ( 2 i + 1 ) ( 2 n − i + 1 ) \frac{1}{2^n}\sum_{i=0}^k{C_n^i(2^i+1)(2^{n-i}+1)} 2n1i=0∑kCni(2i+1)(2n−i+1)
直接求和的时间复杂度仍然是 O ( n × l o g ( n ) ) O(n\times log(n)) O(n×log(n)),肯定会超时,所以我们需要对其进行化简。
= 1 2 n ∑ i = 0 n C n i ( 2 i 2 n − i + 2 i + 2 n − i + 1 ) = 1 2 n [ ∑ i = 0 n C n i 2 n + ∑ i = 0 n 2 i 1 n − i + ∑ i = 0 n 2 n − i 1 i + ∑ i = 0 n C n i ] = 1 2 n [ ∑ i = 0 n C n i 2 n + ( 2 + 1 ) n + ( 1 + 2 ) n + ∑ i = 0 n C n i ] = 1 2 n [ ( 2 n + 1 ) ∑ i = 0 n C n i 1 i 1 n − i + 2 × 3 n ] = 1 2 n [ ( 2 n + 1 ) 2 n + 2 × 3 n ] = 1 2 n ( 4 n + 2 n + 2 × 3 n ) \begin{aligned} &= \frac{1}{2^n} \sum_{i=0}^n{C_n^i (2^i2^{n-i}+2^i+2^{n-i}+1)} \\ &= \frac{1}{2^n} [\sum_{i=0}^n{C_n^i 2^n}+\sum_{i=0}^n{2^i1^{n-i}}+\sum_{i=0}^n{2^{n-i}1^i}+\sum_{i=0}^n{C_n^i}] \\ &= \frac{1}{2^n} [\sum_{i=0}^n{C_n^i2^n}+(2+1)^n+(1+2)^n+\sum_{i=0}^n{C_n^i}] \\ &= \frac{1}{2^n} [(2^n+1)\sum_{i=0}^n{C_n^i1^i1^{n-i}}+2\times 3^n]\\ &= \frac{1}{2^n} [(2^n+1)2^n+2\times 3^n]\\ &= \frac{1}{2^n} (4^n+2^n+2\times 3^n) \end{aligned} =2n1i=0∑nCni(2i2n−i+2i+2n−i+1)=2n1[i=0∑nCni2n+i=0∑n2i1n−i+i=0∑n2n−i1i+i=0∑nCni]=2n1[i=0∑nCni2n+(2+1)n+(1+2)n+i=0∑nCni]=2n1[(2n+1)i=0∑nCni1i1n−i+2×3n]=2n1[(2n+1)2n+2×3n]=2n1(4n+2n+2×3n)
至此我们就可以利用快速幂在 O ( l o g ( n ) ) O(log(n)) O(log(n))的时间复杂度内计算出结果了!
完结撒花o( ̄▽ ̄)ブ
代码
#include <bits/stdc++.h>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
typedef long long LL;
const LL mod = 998244353;
using namespace std;
inline LL read() {
LL x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void write(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
LL quickPow(LL x, LL n, LL mod) {
LL ans = 1;
while(n) {
if(n & 1) ans *= x, ans %= mod;
x *= x, x %= mod;
n >>= 1;
}
return ans;
}
LL inv(LL x) {
return quickPow(x, mod - 2, mod);
}
int main() {
int T = read();
_for(i, T) {
LL k = read();
LL t1 = quickPow(2, k, mod), t2 = t1 * t1 % mod, t3 = 2 * quickPow(3, k, mod);
write((t2 + t1 + t3) % mod * inv(t1) % mod);
puts("");
}
return 0;
}