这种类型的题(对脑电波题)有些题能秒,有些题想多久都想不出来。。。
显然本题能一起染黑的边存在某种关系,我们考虑一条边可以和哪两条边一起染色,乍一看如果还没看出来有什么性质,我们就考虑把连着的边再这样考虑一遍。
突然,灵光乍现!我们这样连可以连出来个斜线!也就是说我们可以将网格图拆开,分开讨论每一个斜线。我们把每个斜线拉直变成序列,发现这显然可以 dp 处理,设 \(f_{i,0}\) 表示到第 \(i\) 位末尾有偶数个点染黑, \(f_{i,1}\) 表示到第 \(i\) 位末尾有奇数个点染黑,转移非常 easy:
\[f_{i,0}=f_{i-1,0}+f_{i-1,1} \]\[f_{i,1}=f_{i-1,0} \]然后我们再回到网格图,手玩一下就会发现分开的斜线含有的边的数量是一个类似分段函数的东西,不妨设 \(n<m\),前 \(n\) 条斜线的长度是差为 \(2\) 的等差数列,后面有 \(m-n\) 条长度为 \(2n+1\) 的斜线,再往后有跟第一段等价的一段。
那么我们预处理出累乘的值,每次询问再用快速幂算出第二段的值就做完啦!
时间复杂度 \(\mathcal{O}(n+T\log n)\)。
代码:
int f[N][2], g[N];
signed main ()
{
f[0][0] = 1;
g[0] = 1;
rep (i, 1, N - 1) f[i][0] = (f[i - 1][0] + f[i - 1][1]) % P, f[i][1] = f[i - 1][0];
rep (i, 1, N - 1) if (i & 1) g[i + 1] = g[i - 1] * f[i + 1][0] % P;
int T = rd ();
for (; T; -- T)
{
int n = rd (), m = rd ();
if (n > m) swap (n, m);
printf ("%lld\n", g[n * 2] * g[n * 2] % P * qpow (f[n * 2 + 1][0], m - n) % P);
}
}
标签:rd,Marking,int,斜线,arc166,RD,ARC166C
From: https://www.cnblogs.com/lalaouyehome/p/18302152