2024省选联测10
A. 小幸运
给出平面上 \(n\) 个点的坐标,以及整数 \(W,H\)。以每个点为底边中点构造底边长度相等且底边与一坐标轴平行的等腰直角三角形,满足三角形在 \((0,0),(W,0),(W,H),(0,H)\) 四点构成的矩形内部且三角形内部区域互不重叠。求每个三角形底边长度的最大值。
把所有坐标扩大两倍后答案只可能是整数。二分答案。每个点拆成 \(4\) 个小三角形,需要选择两个相邻的三角形。判断这 \(4n\) 个三角形是否相交,做 2-SAT。
如果两个三角形有边相交,那么这两个三角形相交。
点击查看代码
// t(): 是否在边界内。
// f(): 是否相交
#define id(x, y) x + y * n
#define fid(x, y) x + y * n + 4 * n
bool check(int d)
{
tot = top = cnt = pos = 0;
for(int i = 1; i <= 8 * n; ++ i) dfn[i] = low[i] = head[i] = 0;
for(int i = 1; i <= n; ++ i)
{
add(id(i, 0), fid(i, 3)); add(fid(i, 0), id(i, 3));
add(id(i, 3), fid(i, 0)); add(fid(i, 3), id(i, 0));
add(id(i, 1), fid(i, 2)); add(fid(i, 1), id(i, 2));
add(id(i, 2), fid(i, 1)); add(fid(i, 2), id(i, 1));
for(int j = 0; j <= 3; ++ j) if(!t(i, j, d)) add(id(i, j), fid(i, j));
}
for(int i = 1; i < n; ++ i)
for(int a = 0; a <= 3; ++ a)
for(int j = i + 1; j <= n; ++ j)
for(int b = 0; b <= 3; ++ b)
if(f(i, a, j, b, d)) add(id(i, a), fid(j, b)), add(id(j, b), fid(i, a));
for(int i = 1; i <= n * 8; ++ i) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n * 4; ++ i) if(sd[i] == sd[i + n * 4]) return 0;
return 1;
}
B. 山河入梦来
给定一个 \(n\times n\) 的矩阵,第 \(i\) 行的第 \(L_i\) 到 \(R_i\) 列为 \(1\),其余为 \(0\)。求行列式。\(n\le 10^5\)
求行列式可以转换成上三角矩阵。
对于 \(L_i\) 相同的一些值,我们会保留 \(R_i\) 最小的行。使用可并堆维护。
点击查看代码
void work()
{
scanf("%d", &n); ans = 1;
for(int i = 1; i <= n; ++ i) rt[i] = 0, p[i] = r[i] = i;
for(int i = 1, l, r; i <= n; ++ i)
scanf("%d%d", &l, &r), t[i] = {r, 1, 0, 0}, rt[l] = merge(rt[l], i);
// p[i] 右端点为 i 的行数
// r[i] 第 i 行的右端点
for(int i = 1; i <= n; ++ i)
{
if(!rt[i]) {ans = 0; break;}
int x = rt[i], w = t[rt[i]].x, y;
if(p[x] != i)
p[y = r[i]] = p[x], r[p[x] = i] = x, r[p[y]] = y, ans *= -1;
rt[i] = merge(t[rt[i]].ls, t[rt[i]].rs);
if(rt[i] && t[rt[i]].x == w) {ans = 0; break;}
rt[w + 1] = merge(rt[w + 1], rt[i]);
}
if(ans == 0) printf("tie\n");
if(ans > 0) printf("pp\n");
if(ans < 0) printf("xx\n");
}
C. 遇见
有一个长度为 \(n\) 的序列,每个元素是 \(1\) 到 \(m\) 的整数。有多少种序列满足在任何一个长度为 \(k\) 的子区间线性相关。
当 \(k > \log_2m\) 时,答案为 \(m^n\)。
当 \(k \le \log_2m\) 时,条件可以转换为任何一个线性无关的子区间的长度都 \(<k\)。
设 \(f_{i,j}\) 表示前 \(i\) 个数,后 \(j\) 个数为极长线性无关的序列个数。
如果新加入一个数没有缩短之前的线性无关区间长度,即与后 \(j\) 个数线性无关。
\(f_{i+1,j+1}+=f_{i,j}\times(m+1-2^j)\)
如果新加入的数与后 \(j\) 个数线性相关,与后 \(t\) 个数线性无关。
\(f_{i+1,t+1}+=f_{i,j}\times2^t\)
答案为 \(\sum_{i<k}f_{n,i}\)
使用矩阵快速幂优化。
点击查看代码
scanf("%lld%lld%lld", &n, &m, &k);
if(k > __lg(m + 1)) {printf("%lld", Pow(m, n)); return 0;}
A.n = 1; A.m = B.n = B.m = k - 1; A.c[1][1] = m;
for(int i = 1; i < k; ++ i)
{
if(i + 1 < k)
B.c[i][i + 1] = (m + 1 - Pow(2, i) + mod) % mod;
for(int j = 1; j <= i; ++ j)
B.c[i][j] = Pow(2, j - 1);
}
A = A * (B ^ n - 1);
for(int i = 1; i < k; ++ i) ans = (ans + A.c[1][i]) % mod;
printf("%lld", ans);