首页 > 其他分享 >组合计数做题笔记

组合计数做题笔记

时间:2024-05-23 13:08:09浏览次数:22  
标签:matrix 组合 color res sum 笔记 计数 int red

\(\color{#FFC116}(1)\) CF1400D Zigzags

  • 给出 \(n\) 个数 \(a_1,a_2,\cdots,a_n\)。求问有多少个四元组 \((i,j,k,l)\),使得这个四元组满足下列条件:

    • \(1 \leq i<j<k<l \leq n\);
    • \(a_i=a_k\) 并且 \(a_j=a_l\)。
  • \(a_i \le n \le 3000\)。

显然可以枚举 \(j, k\),所以此时 \(a_j, a_k\) 为定值。那么 \(i\) 必须要满足 \(i < j\) 且 \(a_i = a_k\),\(l\) 必须要满足 \(l > k\) 且 \(a_l = a_j\)。所以我们要做的就是统计一段区间内某个数字的出现次数。

套路。用 vector 存储每个数字出现的位置,那么每次查询相当于是问某个 vector 中有多少数在 \([l, r]\) 范围内。二分即可。

$\color{blue}\text{Code}$
vector<int> mp[N];

int calc(int l, int r, int x) {
	if (!mp[x].size()) return 0;
	return upper_bound(mp[x].begin(), mp[x].end(), r) - lower_bound(mp[x].begin(), mp[x].end(), l);
}

void Luogu_UID_748509() {
	fin >> n;
	for (int i = 1; i <= n; ++ i ) {
		fin >> a[i];
		mp[a[i]].push_back(i);
	}
	
	res = 0;
	for (int j = 2; j < n; ++ j )
		for (int k = j + 1; k < n; ++ k )
			res += calc(1, j - 1, a[k]) * 1.0 * calc(k + 1, n, a[j]);
	
	fout << res << '\n';
	
	for (int i = 1; i <= n; ++ i ) mp[i].clear();
}

\(\color{#52A41A} (2)\) CF1327E Count The Blocks

  • 对于一个数 \(t\),定义一个块是 \(t\) 中的连续相等的一串数字。写下 \(0\cdots10^n-1\) 的所有数,不足 \(n\) 位的用前导 \(0\) 补足 \(n\) 位。对于每个 \(i=1\cdots n\),求这 \(10^n\) 个数中一共有多少个长为 \(i\) 的块。
  • \(n \le 2 \times 10^5\)。

首先发现 \(10^n\) 个数是假的,你可以将其看作是有 \(n\) 个位置,每个位置可以填 \(0 \sim 9\) 的数字。

显然枚举题中的 \(i\)。分析这 \(n\) 个数字:

  • 有连续的 \(i\) 个数字是相同的,也就是说有 \(10\) 种取值;

  • 这个长度为 \(i\) 的块的左边的位置(可能不存在)不能填这个数字,即有 \(9\) 种取值;

  • 这个长度为 \(i\) 的块的右边的位置(可能不存在)不能填这个数字,即有 \(9\) 种取值;

  • 其余的位置任意填,即每个位置都有 \(10\) 种取值。

然后乘法原理即可。

$\color{blue}\text{Code}$
void Luogu_UID_748509() {
	fin >> n;
	for (int i = 1; i < n; ++ i ) {
		fout << 10ll * ((n - i - 1) * 81ll % P * fpm(10, n - i - 2) % P + 18ll * fpm(10, n - i - 1) % P) % P << ' ';
	}
	puts("10");
}

\(\color{#52A41A}(3)\) CF1545B AquaMoon and Chess

  • 有一个 \(1 \times n\) 的棋盘。有的格子上有棋子。你可以选择一个有棋子的位置 \(i\),并进行以下操作:

    • 如果 \(i+2 \leq n\) 和 \((i+1)\) 上有棋子,且 \((i+2)\) 上没有棋子,将棋子移动到 \((i + 2)\);
    • 如果 \(i-2 \ge 1\) 和 \((i-1)\) 上有棋子,且 \((i-2)\) 上没有棋子,将棋子移动到 \((i - 2)\);

    求最终局面数。

  • \(n \le 10^5\)。

首先发现棋子的移动可以看作是 \([\texttt{1 1}]\) 的移动。例如:

\[\begin{matrix} 0&\color{red}[1&\color{red}1]&0&1\end{matrix} \Longrightarrow \begin{matrix} 0&0&\color{red}[1&\color{red}1]&1\end{matrix} \]

但是还会有一些剩余的 \([\texttt{1}]\),例如:

\[\begin{matrix} \color{red}[1&\color{red}1]&\color{blue}1&0&1\end{matrix} \Longrightarrow \begin{matrix} \color{blue}1&\color{red}[1&\color{red}1]&0&1 \end{matrix} \Longrightarrow \begin{matrix} \color{blue}1&0&\color{red}[1&\color{red}1]&1 \end{matrix} \]

发现这个单独的 \([\texttt{1}]\) 的位置是可以随 \([\texttt{1 1}]\) 而唯一确定的。所以我们只需要考虑 \([\texttt{1 1}]\)。

设有 \(a\) 个 \([\texttt{1 1}]\)(也就是有 \(2a\) 个数),\(b\) 个剩余的 \([\texttt1]\),那么答案为 \(\dbinom{n-a-b}a\),表示将这 \(a\) 个 \([\texttt{1 1}]\) 随便放。

$\color{blue}\text{Code}$
void Luogu_UID_748509() {
	scanf("%d%s", &n, s + 1);
	int a = 0, b = 0;
	for (int i = 1; i <= n; ++ i ) {
		if (i < n && s[i] == '1' && s[i + 1] == '1') {
			++ a;
			++ i;
		}
		else if (s[i] == '1') {
			++ b;
		}
	}

	fout << C(n - a - b, a) << '\n';
}

\(\color{#9D3DCF}(4)\) CF1245F Daniel and Spring Cleaning

  • 给定 \(l, r\),求 \(\sum_{i=l}^r \sum_{j=l}^r [i+j = i \operatorname{xor} j]\)。
  • \(r \le 10^9\)。

因为异或是不进位加法,所以只要 \(i, j\) 进行加法时不进位,就一定有 \(i+j = i \operatorname{xor} j\)。换言之,\(i \operatorname{and} j = 0\) 和 \(i+j = i \operatorname{xor} j\) 是等价的。

所以所求即 \(\sum_{i=l}^r \sum_{j=l}^r [i \operatorname{and} j = 0]\)。

设 \(f(x, y) = \sum_{i=\color{red}1}^x \sum_{j=\color{red}1}^y [i \operatorname{and} j = 0]\)。那么所求即 \(f(r, r) - f(r, l - 1) - f(l - 1, r) + f(l - 1, l - 1)\)。\(f(x, y)\) 的求解是简单数位 DP。

$\color{blue}\text{Code}$
int a[50], b[50], len;
int f[50][2][2];

int dp(int u, bool sb, bool bs) {
	if (!u) return 1;
	
	int& res = f[u][sb][bs];
	if (~res) return res;
	res = 0;
	
	int t1 = sb ? a[u] : 1;
	int t2 = bs ? b[u] : 1;
	
	for (int i = 0; i <= t1; ++ i )
		for (int j = 0; j <= t2; ++ j )
			if (!i || !j) res += dp(u - 1, sb && (i == t1), bs && (j == t2));
	
	return res;
}

int calc(int x, int y) {
	if (x < 0 || y < 0) return 0;
	
	memset(f, -1, sizeof f);
	memset(a, 0, sizeof a);
	memset(b, 0, sizeof b);
	
	int l = 0;
	while (x) {
		a[ ++ l] = x & 1;
		x >>= 1;
	}
	len = l, l = 0;
	while (y) {
		b[ ++ l] = y & 1;
		y >>= 1;
	}
	len = max(len, l);
	
	int res = dp(len, 1, 1);
	
	return dp(len, 1, 1);
}

void Luogu_UID_748509() {
	int l, r;
	fin >> l >> r;
	fout << (calc(l - 1, l - 1) + calc(r, r)) - (calc(l - 1, r) + calc(r, l - 1)) << '\n';
}

\(\color{#52A41A}(5)\) CF1444B Divide and Sum

  • 给一个长度为 \(2n\) 的数列 \(a\),将 \(a\) 中的数分为两串长度为 \(n\) 的数列 \(p\) 和 \(q\)。将 \(p\) 升序排序,\(q\) 降序排序。定义权值为 \(\sum_{i=1}^n |p_i - q_i|\)。求所有划分的权值和。
  • \(n \le 1.5 \times 10^5\)。

首先每种划分的方案都是一定的,为 \(\sum_{v \in S_1} v - \sum_{v \in S_2} v\)。其中 \(S_1\) 表示 \(a\) 的前 \(n\) 大的数构成的集合,\(S_2\) 表示 \(a\) 的前 \(n\) 小的数构成的集合。

那么答案为 \(\dbinom {2n}n \cdot \sum_{v \in S_1} v - \sum_{v \in S_2} v\)。

首先 \(\sum |p_i - q_i| = \sum (\max (p_i,q_i) - \min(p_i, q_i))\),而且可以证明 \(\max (p_i,q_i) \in S_1\) 以及 \(\min(p_i, q_i) \in S_2\)。

考虑反证:

  • 如果 \(\max (p_i,q_i), \min(p_i, q_i) \in S_1\),那么一定存在某个 \(j\) 使得 \(\max (p_j, q_j), \min(p_j, q_j) \in S_2\)。那么此时 \(i < j\) 或 \(i > j\) 都不能满足「\(p\) 升序排序,\(q\) 降序排序」这个条件。
  • 如果 \(\max (p_i,q_i), \min(p_i, q_i) \in S_2\),那么一定存在某个 \(j\) 使得 \(\max (p_j, q_j), \min(p_j, q_j) \in S_1\)。那么此时 \(i < j\) 或 \(i > j\) 都不能满足「\(p\) 升序排序,\(q\) 降序排序」这个条件。
$\color{blue}\text{Code}$
void Luogu_UID_748509() {
	fin >> n;
	for (int i = 1; i <= n * 2; ++ i ) fin >> a[i];
	sort(a + 1, a + n * 2 + 1);
	int res = 0;
	for (int i = 1; i <= n; ++ i ) (res += a[i + n] - a[i]) %= P;
	fout << (ll)res * C(2 * n, n) % P << '\n';
}

标签:matrix,组合,color,res,sum,笔记,计数,int,red
From: https://www.cnblogs.com/2huk/p/18208185

相关文章

  • 学习笔记:树与图上的计数问题
    Prüfer序列\(n\)个点的有标号无根树可以与一个长度为\(n-2\)的Prüfer序列对应。从树到Prüfer序列\(f\)为空序列。如果当前树上多于两个节点,假设当前标号最小的叶子为\(x\),与\(x\)相连的节点标号为\(y\),那么把\(x\)从树上删除,把\(y\)加入\(f\)末尾。......
  • QGIS开发笔记(二):Windows安装版二次开发环境搭建(上):安装OSGeo4W运行依赖其Qt的基础环境De
    前言  使用QGis的目的是进行二次开发,或者说是融入我们的应用(无人车、无人船、无人机),本片描述搭建QGis二次基础开发环境,由于实在是太长了,进行了分篇:上半部分:主要是安装好后,使用QtCreator可以使用QGIs的apps下的Qt使用对应的编译器编译不带qgis的空工程。下半部分:在上半......
  • U-BERT论文阅读笔记
    U-BERT:Pre-trainingUserRepresentationsforImprovedRecommendation论文阅读笔记Abstract学习用户表征是推荐系统的一项关键任务,因为它可以编码用户对个性化服务的偏好。用户表征一般是从点击互动和评论意见等行为数据中学习的。然而,对于不太流行的领域,行为数据不足以学习......
  • .NetCore源码阅读笔记系列之Security (二) 自定义认证实践
    通过前面对AddCookie或者 AddOpenIdConnect等了解,其实里面都实现了一个AuthenticationHandler<TOptions>的认证处理,接下来我们来简单自定义一个试试首先我来实现下面这个方式,我添加了一个AddLIYOUMING()services.AddAuthentication(options=>{......
  • 《Linux内核完全注释》学习笔记:2.4 Linux内核进程控制
    程序是一个可执行的文件,而进程(process)是一个执行中的程序实例。利用分时技术,在Linux操作系统上同时可以运行多个进程。分时技术的基本原理:把CPU的运行时间划分成一个个规定长度的时间片(timeslice),让每个进程在一个时间片内运行。当进程的时间片用完时系统就利用调度程序......
  • P2606 [ZJOI2010] 排列计数
    P2606[ZJOI2010]排列计数树形dp序列中每个位置的限制只有另外一个位置,那么我们将这样的限制连线,就可以得到一棵树。在这题中,这棵树刚好是小根堆,一棵完全二叉树。题目就转化为一共有多少种小根堆。那么显然的\(a_1=1\),然后左子树和右子树分剩下的\([2,n]\),并且左右子树不互......
  • 梦断代码阅读笔记02
    梦断代码阅读笔记02技术开发与创新在过去的技术开发中,我通常会选取最熟悉或最简单的解决方案,避免过多的风险和试验新的技术。这种保守的作风虽然保证了项目的短期进度,但却限制了技术创新和自身成长。结合《梦断代码》一书中的案例,可以看到这种方式存在诸多弊端。书中描述了Eagl......
  • Performance选项卡笔记以及分析vue页面卡顿
    各区域内容说明Performance选项卡可以用于分析页面性能。最上方红框区域内出现红色块的代表该时间段帧率不足60帧。往下是cpu占用率。卡顿原因主要耗时根据以下该图进行分析。问题分析由此可见本次分析主要导致卡顿的原因是因为js的执行所导致的。可以选择时间区域进一......
  • 反悔贪心学习笔记
    本文仅用于笔者关于反悔贪心的学习笔记,反悔贪心是笔者在一场$div4$中遇到的问题,故来学习一番【学习笔记】反悔贪心 1、什么是反悔贪心?贪心本身是没有反悔操作的,贪心求的就是当前的最优解。但当前的最优解有可能是局部最优解,而不是全局最优解,这时候就要进行反悔操......
  • 学习笔记:集合幂级数与 FWT
    集合幂级数集合到整数设\(n\)元集\(A=a_1,a_2,\cdots,a_n\),定义\(A\)的幂集\(2^{A}=\{S\midS\subseteqA\}\)到整数集\(\mathbb{Z}\)的映射\(\text{id}\)为:若\(S=\{a_{i_1},a_{i_2},\cdots,a_{i_k}\}\),则\(\text{id}(S)=\sum_{j=1}^{k}2^......