2024年锦鲤化龙网络赛

A 自相残杀:题目链接

题目大意:

\(n \times n\) 的棋盘上摆放 \(k\) 头恶龙,使得每头恶龙都能相互攻击到对方的方案数。

解题思路:

模拟,找规律。

  • \(k\gt 4\) 时,无论怎么摆放,方案数都是 \(0\) 种,直接输出 \(0\)

  • \(k \le 4\) 时,分类讨论:

    • \(o\) 表示摆放恶龙的位置,\(x\) 表示空白棋盘

    • \(k = 0\)\(0\) 种方案

    • \(k =1\)\(n \times n\) 种方案,每个棋盘的一个格子就是一种方案

    • \(k =2\)\(2\times (2n-1) \times(n - 1)\) 种方案,只有 \(4\) 种摆放方式

      • \(\begin{matrix}o&x\\o&x\end{matrix}\)
      • \(\begin{matrix}o&o\\x&x\end{matrix}\)
      • \(\begin{matrix}o&x\\x&o\end{matrix}\)
      • \(\begin{matrix}x&o\\o&x\end{matrix}\)
    • \(k=3\)\(4\times (n-1)\times(n-1)\) 种方案,只有 \(4\) 种摆放方式

      • \(\begin{matrix}o&o\\o&x\end{matrix}\)
      • \(\begin{matrix}o&o\\x&o\end{matrix}\)
      • \(\begin{matrix}o&x\\o&o\end{matrix}\)
      • \(\begin{matrix}x&o\\o&o\end{matrix}\)
    • \(k=4\)\((n-1)\times(n-1)\) 种方案,只有一种摆放方式

      • \(\begin{matrix}o&o\\o&o\end{matrix}\)

B 暴躁兔兔:题目链接

题目大意:

\(C\) 只兔子放到 \(N\) 个兔笼里,使得每个兔子之间相隔的最近距离最大。

解题思路:

二分答案

首先数据不是有序的,先排序sort(a + 1, a + 1 + n)

可以知道答案的左边界是:\(1\) ,答案的右边界是:\(a_n-a_1\)

接下来二分,\(check\) 函数

bool check(int x)
{
	int cnt = 1, last = a[1]; // 上一次放兔子的位置
	for (int i = 2; i <= n; i++)
	{
		if (a[i] - last >= x)
		{
			cnt++;
			last = a[i];
		}
	}
	return cnt >= m; // true -> l = mid + 1; false -> r = mid - 1;
}
...
while (l <= r)
{
	int mid = (long long)(l + r) / 2;
	if (check(mid)) l = mid + 1;
	else r = mid - 1;
}
cout << r << endl;

C 灯火通明:题目链接

题目大意:

使得两列灯笼距离最小的交换相邻灯笼的次数。

解题思路:

贪心:首先想到一定是高度一一对应,小对小,大对大。

离散化:通过映射下标,使两列灯笼相关联, c[第一列灯笼的下标]=第二列灯笼的下标

  • 举个例子:

  • 4
    1 3 4 2 -> 排序后:1 2 3 4,下标为:1 4 2 3
    1 7 2 4 -> 排序后:1 2 4 7,下标为:1 3 4 2
    接下来进行下标的关联
    c[1] = 1
    c[4] = 3
    c[2] = 4
    c[3] = 2
    所以c数组为 [1,4,2,3]
    
  • 我们求的交换次数,就是 \(c\) 数组的逆序对的个数。

至此:问题转化为求 \(c\) 数组的逆序对的个数。

D 猜灯谜啦:题目链接

题目大意:

求区间 \([L,R]\) 内可以由 \(B\) 进制表示成有 \(K\)\(1\) 的数的个数。

如:\((17)_{10}=(10001)_2\) \((18)_{10}=(10010)_2\) \((20)_{10}=(10100)_2\)

解题思路:

考虑 $1 $ ~ \(n\) 满足条件的数的个数 dp(n)

所以我们的所求为:dp(R) - dp(L-1)

组合数的初始化,使用递推公式即可。

考虑组合数,我们对这个数字转换成 \(B\) 进制后的每一位进行处理,假设分解后的位数为 \(n+1\) 位,\(last\) 为可取 \(1\) 的个数。

  • 这一位是 \(0\) ,那么可以考虑从后面的所有位中选 \(K-last\) 个数的选法,\(C_i^{k-last}\)
  • 这一位 \(\gt 1\) ,比如 \(4\) ,那么可以取 \(1,2,3,4\)
    • \(1\) 后,剩下的只能是后面所有的位中选 \(K-last-1\) 个数, \(C_i^{k-last-1}\)
    • 为什么不考虑其他情况,因为本题是求 \(1\) 的个数
  • 这一位 \(=1\) ,直接取 \(last\)++,为啥不能和上面的情况归到一类呢?因为我们要保证组合出来的数都要比 \(n\) 小,如果直接组合数的话,可能组合出来的就比 \(n\) 大了,所以我们要在下一位判断。

把上述的情况加起来,还要算上最后一种:取到最后一位,并且 \(last\)\(K\) 了,答案加 \(1\)

核心代码:

const int N = 35;
int f[N][N]; // 存组合数
int X, Y, K, B;
void init() // 初始化组合数
{
	for (int i = 0; i < N; i++)
		for (int j = 0; j <= i; j++)
			if (!j) f[i][j] = 1;
			else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}

int dp(int n)
{
	if (!n) return 0; // 边界判断,n为0
	vector<int> nums; // 存B进制下分解后的每一位的数字
	
	while (n) nums.pb(n % B), n /= B;
	
	int res = 0;
	int last = 0; // 取1的个数
	for (int i = nums.size() - 1; i >= 0; i--)
	{
		int x = nums[i];
		if (x) // x不为0
		{
			res += f[i][K - last]; // 当前取0
			if (x > 1) // 当这一位 > 1 ,我们只能取 1
			{
				if (K - last - 1 >= 0) res += f[i][K - last - 1];
				break; // 因为我们是从最高位依次判断的,所以我们可以直接算出来后面的方案数,因为那些方案组成的数字一定不会超过原来的数字。
			}
			else // 等于1的情况
			{
				last++;
				if (last > K) break;
			}
		}
		if (!i && last == K) res++;	// 到了这里,说明原来的那个数n也是满足条件的,要加一个
	}
	
	return res;
}

E 龙登天梯:题目链接

题目大意:

求斐波那契数列的第 \(n\)

解题思路:

递推表达式,注意写高精度。f[i] = f[i - 1] + f[i - 2] 这里使用高精度加法。

也可以使用 python 直接写斐波那契数列 hhh

posted @ 2024-02-08 21:54  高明y  阅读(39)  评论(0)    收藏  举报