[总结] 不是科技
双线翻折
前言
一次翻折可以帮助求出卡特兰数的一个公式:\(F(n)=\binom{2n}{n}-\binom{2n}{n-1}\)。
就是将卡特兰数与从 \((1,1)\) 走到 \((n,n)\) 且不碰到 \(y=x+1\) 的路径方案一一对应。
具体求法是容斥,首先总的方案是 \(\binom{2n}{n}\),然后考虑减去碰到了 \(y=x+1\) 的方案;
考虑直接将终点按 \(y=x+1\) 对称,这样到新终点就必定会碰到这条直线,同时不难发现到新终点的每条路径都唯一对应原来一条非法路径。
拓展
-
对于从 \((0,0)\) 走到 \((n,m)\) 的不降路径进行计数,要求不能碰到 \(y=x+r\),那么将起点按直线对称为 \((-r,r)\),那么答案即为 \(\binom{n+m}{n}-\binom{n+m}{n+r}\)。
-
若不能碰到的线有两条,则使用双线翻折,把终点交替按两条线翻折,翻折后计算得出的路径与原来按交替顺序碰到这两条线的路径一一对应。
int x = n, y = n, ans = C(x + y, y), p;
auto rev1 = [&] (int &x, int &y) -> void{ swap(x, y), x -= 1, y += 1; };
auto rev2 = [&] (int &x, int &y) -> void{ swap(x, y), x += m, y -= m; };
p = 1, x = n, y = n;
while(x >= 0 && y >= 0){
if(p) rev1(x, y), MOD(ans -= C(x + y, y));
else rev2(x, y), MOD(ans += C(x + y, y) - mod);
p ^= 1;
}
p = 0, x = n, y = n;
while(x >= 0 && y >= 0){
if(p) rev1(x, y), MOD(ans += C(x + y, y) - mod);
else rev2(x, y), MOD(ans -= C(x + y, y));
p ^= 1;
}
cout << ans << endl;
\(O(\log)\) 查两个有序序列第 \(k\) 大
介绍
给你两个数组 \(a,b\),长度分别为 \(n,m\),现在要求第 \(k\) 大。
\(\log^2\) 的做法很常见,先二分第 \(k\) 大是多少,然后再二分有多少个数小于当前二分的值。
但是还有个 \(\log\) 做法是这样的:
int solve(int *a, int *b, int n, int m, int k){
if(k == 1) return n && m ? min(a[1], b[1]) : n ? a[1] : b[1];
int p = k >> 1; k -= p;
int v1 = p <= n ? a[p] : inf;
int v2 = p <= m ? b[p] : inf;
if(v1 < v2) a += p, n -= p; else b += p, m -= p;
return solve(a, b, n, m, k);
}
拓展
\(O(n)-O(1)\) 求 \(\le n\) 的两数的 \(\gcd\)
对于任意整数 \(n\),可以表示为 \(abc=n\),满足 \(a,b,c\) 要么 \(\le \sqrt n\) 要么是质数。
分解方式:分解质因数,放入当前 \(a,b,c\) 中最小的那个中。
那么计算 \(\gcd(x,y)\) 时,设 \(x=abc\),显然可以分别计算 \(\gcd(a,y),\gcd(b,\dfrac{y}{(a,y)}),\gcd(c,\dfrac{y}{(ab,y)})\).
那么质数情况直接判断,否则调用预处理的 \(\le \sqrt{n}\) 的两两 \(\gcd\)。
高斯整数的 \(\gcd\)
高斯整数指的是形如 \(x+yi\) 的整数,其 \(\gcd\) 仍然可以使用辗转相除求得。
其意义在于,可以求出两个向量的 \(\gcd\)。
struct Cpx{
LL x, y;
Cpx operator +(Cpx t){ return {x + t.x, y + t.y}; }
Cpx operator -(Cpx t){ return {x - t.x, y - t.y}; }
Cpx operator *(Cpx t){ return {x * t.x - y * t.y, y * t.x + x * t.y}; }
Cpx operator /(Cpx t){
LD a = x, b = y, c = t.x, d = t.y;
return {(LL)roundl((a * c + b * d) / (c * c + d * d)),
(LL)roundl((b * c - a * d) / (c * c + d * d))};
}
Cpx operator %(Cpx t){ return *this - (*this / t) * t; }
LL len(){ return x * x + y * y; }
};
inline Cpx gcd(Cpx x, Cpx y){ Cpx t; while(y.len()){ t = x, x = y, y = t % y; } return x; }
射线法
问题模型
给出一个 \(n\times m\) 的网格图,起点为 \(sx,sy\) 要走一条路径使其是封闭图形 (即最后仍回到起点)。
其中有 \(k\) 个特殊位置,对于其是否包含在围出的封闭图形中会有相关要求。
同时一般还要求在某些同类情况最小化行走的步数。
处理方式
根据计算几何知识,可以用射线法判断一个点是否被封闭图形包含。
不妨对于点 \((x,y)\) 引一条向上的射线,若其穿过我们行走的奇数次则被包含,否则不包含。
那么状态就是 \(f(x,y,S)\),表示现在的位置是 \((x,y)\),\(S\) 集合中的点目前是被包含的。
每次行走一步就判断有多少点的状态改变了即可。