题目定位:欺诈题
题目难度:普及/提高–
注意到题目里得分的构造方式,我们可以发现只要答案给错了得分一定比较少,因此我们必须每次给出正确的答案。
然后,我们观察得分的形式:由于
2
n
2^n
2n的乘数因子是固定的,因此我们可以把它提出来,问题等价于只要求方案数即可。由一些组合数最基本的定义得到第
i
i
i个路口的答案一定是
C
n
i
C^i_n
Cni。最终答案应该是
∑
i
=
0
n
C
n
i
\sum\limits_{i=0}^{n}C^i_n
i=0∑nCni。然后把它摆在杨辉三角形里相当于求杨辉三角形整行的和,因此我们由组合恒等式
∑
i
=
0
n
C
n
i
=
2
n
\sum\limits_{i=0}^{n}C^i_n=2^{n}
i=0∑nCni=2n即可将问题转化为求
2
2
n
%
998244353
=
4
n
%
998244353
2^{2n}\%998244353=4^{n}\%998244353
22n%998244353=4n%998244353的结果。
注意到
998244353
998244353
998244353是个质数,我们由费马小定理知道
4
998244352
%
998244353
=
1
4^{998244352}\%998244353=1
4998244352%998244353=1,所以我们只需要将
n
n
n对
998244353
998244353
998244353取模然后快速幂计算结果即可。
然而考虑到这个
n
n
n实在是太大了,所以我们必须要采用如下的方式来解决:
输入文件总计
10
M
B
10MB
10MB左右,换算之:
10
∗
1024
∗
1024
≈
1
0
7
10*1024*1024\approx10^7
10∗1024∗1024≈107,大约是
1
0
7
10^7
107个数字。我们有如下的解决方案:
1.对于
C
+
+
C++
C++选手你可以考虑在快速读入时做这样的改变。
这是原本的快速读入:
#define Fermat 998244352
#define mod 998244353
inline int read()
{
int n=0,k=1;
char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9') n=(n<<1)+(n<<3)+(c^48),c=getchar();
return n;
}
如果我们加费马小定理优先取模,可以这么做:
#define Fermat 998244352
#define mod 998244353
inline int read()
{
int n=0,k=1;
char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9') n=(n<<1)+(n<<3)+(c^48),n%=Fermat,c=getchar();
return n;
}
主程序中调用
int n=read();
这样可以直接得到对
998244352
998244352
998244352取模过后的
n
n
n
当然你想直接字符串读进来写个高精对单精取模也是可以的。
2.对于Pascal选手,你没有可以这么做的快速读入,但你仍然可以这么做
while not eof do
begin
read(c);
n:=n*10+odd(c)-48;
n:=n mod 998244352;
end;
(我没有卡语言,我很良心)
3.对于Python,Java等语言的选手:你们可以使用BigInt,当然只要你不怕TLE
另外,如果你们注意到Pascal,Python和Java(如果有)等语言的程序始终因为TLE而无法通过,你可以将运行时间限制调整到 2 s 2s 2s