首页 > 其他分享 >计数题选做

计数题选做

时间:2023-07-31 12:22:54浏览次数:37  
标签:cnt 题选 int long 计数 vector using dp

[ABC267G] Increasing K Times

Difficulty: *2561。

题目所求即为重排 \(a\),使得满足 \(a_i<a_{i+1}\) 的下标 \(i\) 恰有 \(k\) 个的方案数。

容易发现,\(a\) 的顺序其实没有影响,可以直接先将 \(a\) 排序。

设 \(dp_{i,j}\) 表示前 \(i\) 个数,恰有 \(j\) 个下标满足 \(a_k<a_{k+1}\) 的方案数,考虑将 \(a_i\) 插入,则可能会产生两种贡献:

  1. 若将 \(a_i\) 插在某个顺序对中间,则该顺序对中较小的元素一定 \(<a_i\),较大的元素一定 \(\ge a_i\),此时顺序对个数变化 \(+1-1=0\)。
  2. 若将 \(a_i\) 插在某个非顺序对中间,假设其为 \(a_p,a_{p+1}\)(\(a_p\ge a_{p+1}\))。
    • 若 \(a_p =a_i\ge a_{p+1}\),此时顺序对个数没有变化;
    • 若 \(a_p<a_i>a_{p+1}\),此时顺序对个数 \(+1\)。
  3. 若将 \(a_i\) 插在头部,顺序对个数没有变化。
  4. 若将 \(a_i\) 插在尾部,若 \(a_{i-1}=a_i\) 则顺序对个数不变,否则 \(+1\)。这种情况可以和情况 2 合并。

因此,设此时 \(a_i\) 共出现 \(cnt_i\) 次,则 \(j\) 不变的情况共有 \(j+1+cnt_{a_i}\) 种,\(j\) 增加 \(1\) 的情况共有 \((i+1)-(j+1+cnt_{a_i})\) 种。转移方程:

\[dp_{i,j}=dp_{i-1,j}\cdot(j+1+cnt_{a_i})+dp_{i-1,j-1}\cdot (i-j-cnt_{a_i}) \]

直接转移即可,时间复杂度 \(\mathcal{O}(n^2)\)。

Code
#include <bits/stdc++.h>
#define all(s) s.begin(), s.end()
using namespace std;
using ll = long long;
using ull = unsigned long long;

const int _N = 1e5 + 5;
const int MOD = 998244353;

int T;

void solve() {
	int n, k; cin >> n >> k;
	vector<int> a(n + 1), cnt(n + 1);
	for (int i = 1; i <= n; i++) cin >> a[i];
	sort(a.begin() + 1, a.end());
	vector<vector<ll>> dp(n + 1, vector<ll>(k + 1, 0));
	dp[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= k; j++) {
			dp[i][j] += dp[i - 1][j] * (j + 1 + cnt[a[i]]) % MOD;
			if (j > 0) dp[i][j] += dp[i - 1][j - 1] * (i - j - cnt[a[i]]) % MOD;
			dp[i][j] %= MOD;
		}
		cnt[a[i]]++;
	}
	cout << dp[n][k] << '\n';
	return;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	// cin >> T;
	T = 1;
	while (T--) {
		solve();
	}
}

[CF1612G] Max Sum Array

Difficulty: *2500。

假设 \(i\) 在 \(a\) 中出现的位置分别为 \(p_1,p_2,\dots,p_{c_i}\)(\(p_1<p_2<\cdots<p_{c_i}\)),则产生的总贡献为

\[\sum_{1\le j<k\le c_i} p_k-p_j \]

对于每项算出贡献,即为

\[\sum_{1\le j\le c_i}(j-1)p_j-(c_i-j)p_j=\sum_{1\le j\le c_i}(2j-c_i-1) p_j \]

对于 \(a\) 中的单个元素算贡献,可以把前面的 \((2j-c_i-1)\) 看成权值,贪心地把权值小的放在前面即可,方案数即为所有权值相同段长度的阶乘之积。

由于暴力的复杂度和 \(\sum c_i\) 有关,我们可以直接记录每个权值下有多少下标,权值最多有 \(2\cdot \max c_i\) 种,直接扫一遍即可。加的时候可以分奇偶差分,时间复杂度为 \(\mathcal{O}(m+\max c_i)\)。

Code
#include <bits/stdc++.h>
#define all(s) s.begin(), s.end()
#define int long long
using namespace std;
using ll = long long;
using ull = unsigned long long;

const int _N = 1e5 + 5;
const int MOD = 1e9 + 7;

int T;

void solve() {
	int m; cin >> m;
	vector<int> c(m + 1);
	int mx = 0;
	for (int i = 1; i <= m; i++) cin >> c[i], mx = max(mx, c[i]);
	vector<int> bucket(2 * mx + 2);
	for (int i = 1; i <= m; i++) {
		int s = mx - c[i] + 1, t = mx + c[i] - 1;
		bucket[s]++, bucket[t + 2]--;
	}
	int len = 0;
	for (int i = 1; i < 2 * mx; i++) {
		if (i > 1) bucket[i] += bucket[i - 2];
		len = max(len, bucket[i]);
	}
	vector<int> fac(len + 1, 1);
	for (int i = 1; i <= len; i++) fac[i] = 1ll * i * fac[i - 1] % MOD;
	int cnt = 1; ll val = 0, ans = 1;
	for (int i = 1; i < 2 * mx; i++) {
		val += (1ll * (cnt + cnt + bucket[i] - 1) * bucket[i] / 2) % MOD * (i - mx) % MOD;
		val %= MOD;
		ans *= fac[bucket[i]];
		ans %= MOD;
		cnt += bucket[i];
	}
	cout << val << ' ' << ans << '\n';
	return;
}

signed main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	// cin >> T;
	T = 1;
	while (T--) {
		solve();
	}
}

标签:cnt,题选,int,long,计数,vector,using,dp
From: https://www.cnblogs.com/Error-Yuan/p/17592278.html

相关文章

  • java统计数据库字段
    packagedb;importjava.sql.*;importjava.util.ArrayList;importjava.util.List;/***@Author:dominic**/publicclassStatistic{publicstaticvoidmain(String[]args)throwsSQLException,ClassNotFoundException{Stringa="x......
  • 2.1.1 进位计数制
    最古老的计数方法十进制计数法进位计数制:有0~9,共十种符号。逢十进一推广:r进制计数法二进制←→八进制、十六进制各种进制的常见书写方式十进制→任意进制真值和机器数知识回顾注意:有的十进制小数无法用二进制精确表示。如:0.3......
  • sql server 存储过程 计数
    SQLServer存储过程计数的实现介绍在SQLServer中,存储过程是一种可重复使用的数据库对象,可以接受参数并返回结果。存储过程可以包含一系列的SQL语句,用于完成特定的数据库操作。在本文中,我们将讨论如何编写一个存储过程来实现计数功能。流程下面是实现SQLServer存储过程......
  • 计数dp
    错位排列计数(组合意义dp)题目给定长度为\(n\)的排列,求解其错位排列数题解设\(D_n\)表示长度为\(n\)的排列的错排数,考虑我们已经知道了前\(n-1\)个错排数,那么对新加入的这第\(n\)个数进行分类讨论直接与前面的数交换,有\((n-1)\timesD_{n-2}\)种方案放在前......
  • 海亮 7.24 水题选讲
    海亮7.24水题选讲TheMaximumPrefix我们设定一个状态\(f_{i,j}\)表示这个序列的\([i+1,n]\)区间的最大前缀和为\(j\),这个序列的期望得分。转移为\(f_{i,j}=f_{i-1,j+1}\timesp_i+f_{i-1,\max\left(j-1,0\right)}\times\left(1-p_i\right)\)。第一个整式表示第\(i\)......
  • 计数题目合集
    CF1342F题目链接很巧妙的一个计数题。原题目等价于构建一个递增序列\(p\),赋予每个数字一个属性\(b\),满足\(b_{p_i}=p_i\),其余任选。且\(\forallj,\sum\limits_{i=1}^n[b_i=p_j]a_i<\sum\limits_{i=1}^n[b_i=p_{j+1}]a_i\)。最大化递增序列\(p\)的长度。先考虑一个朴......
  • 【模板】图的计数相关:行列式及求值、Matrix-Tree 定理、BEST 定理、LGV 引理
    归类为线性代数、图论。证明都是神仙,特别是名字带“理”的,不证了。行列式定义行列式(Determinant)是对\(n\)阶方阵\(A\)定义的,是一个标量。\(A\)的\(n\)阶行列式\(det(A)\)或\(|A|\)定义如下:\[det(A)=\sum_p(-1)^{\mu(p)}\prod_{i}A[i][p_i].\]这里将排列的逆序对数......
  • abc090d <枚举计数>
    题目D-RemainderReminder代码Code//https://atcoder.jp/contests/abc090/tasks/arc091_b#include<iostream>#include<algorithm>#include<vector>#include<cstring>usingnamespacestd;usingLL=longlong;usingPII=pair<in......
  • Luogu 5296 生成树计数
    好像有道题是求生成树权值和的和的,考虑\(\sum\limits_{T}\sum\limits_{e\inE(T)}w_e\)咋做。每条边给一个边权\(v_e(x)=1+w_ex\),然后跑矩阵树:\[\text{ans}=[x]\sum\limits_{T}\prod\limits_{e\inE(T)}v_e(x)\]受到来自神秘力量的启示,我们考虑类似上面这样构造边权。考虑......
  • Burnside定理和Polya计数
    置换群Burnside定理和Polya计数都需要运用置换群的知识置换群主要有三种运算,分别是合成运算、恒等置换、置换的逆运用着三种运算就可以推导出Burnside定理和Polya计数的公式Burnside定理Burnside定理的主要应用是循环排列计数、项链计数、正五角形着色等下面给出一道例题P......