ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AtCoder Beginner Contest 215

2021-08-24 22:01:27  阅读:184  来源: 互联网

标签:AtCoder 215 return Beginner int sum binom include MOD


AtCoder Beginner Contest 215

菜的本质暴露的一览无余(

一场 ABC 一场 ARC 排名都 1k+,直接掉了 200 多分 QwQ

H 是比较神仙的数数题,所以锅了。

\(A\sim D\)

\(A\sim C\) 就简单模拟,\(D\) 简单筛一下。

\(E\)

给定字符串序列,只包含前 \(10\) 个大写字母 \(A\sim J\),求合法的子序列个数。

一个子序列是合法的,当且仅当它当中的相同字符都是连续的,例如 AABCD 合法,但 ABCDA 不合法。

真·考场降智,认为 \(2^{10}\) 超级大,根本不可能状压/fad

显然就简单状压 DP,设 \(f(i,S,x)\) 表示 \(1\sim i\) 中,出现过的字符集合为 \(S\),最后一个字符为 \(x\) 的子序列个数。

简单转移即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 1010;
const LL MOD = 998244353;
int n; char str[N];
LL f[N][1 << 10][10];

void P(LL &x, LL y) {x = (x + y) % MOD;}

int main() {
	scanf("%d %s", &n, str + 1);
	for(int i = 1; i <= n; i ++) {
		int c = str[i] - 'A';
		for(int s = 0; s < (1 << 10); s ++)
			for(int t = 0; t < 10; t ++) if(f[i - 1][s][t]) {
				P(f[i][s][t], f[i - 1][s][t]);
				if(!(s >> c & 1) || (t == c)) 
					P(f[i][s | (1 << c)][c], f[i - 1][s][t]);
			}
		P(f[i][1 << c][c], 1);
	}
	LL ans = 0;
	for(int s = 0; s < (1 << 10); s ++)
			for(int t = 0; t < 10; t ++) P(ans, f[n][s][t]);
	printf("%lld\n", ans);
	return 0;
}

\(F\)

求平面最远点对,其中两点 \((x_1,y_1),(x_2,y_2)\) 距离定义为 \(\min(|x_1-x_2|,|y_1-y_2|)\)。

因为没想出来 E 所以比赛后期思维挺乱的,跳到这道题之后一直想着分治做法。

实际上是简单的二分答案,将点按 \(x\) 升序排序,然后双指针扫描一遍。

得到里当前点 \(x\) 的差距最小的,\(x\) 维上的距离 \(\geq mid\) 的点,预处理一个 \(y\) 的后缀 \(\min,\max\) 即可简单判断。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 2e5 + 10;
int n, Mx[N], Mn[N];
struct Node{int x, y;} a[N];

int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(Node a, Node b) {return a.x < b.x;}
int Abs(int x) {return x < 0 ? - x : x;}

bool Chck(int mid) {
	int r = 1;
	for(int l = 1; l <= n; l ++) {
		while(r <= n && a[r].x - a[l].x < mid) r ++;
		if(r >  n) return false;
		if(r <= n) {
			if(Abs(a[l].y - Mx[r]) >= mid) return true;
			if(Abs(a[l].y - Mn[r]) >= mid) return true;
		}
	}
	return false;
}

int main() {
	n = read();
	for(int i = 1; i <= n; i ++) a[i].x = read(), a[i].y = read();
	sort(a + 1, a + n + 1, cmp);
	Mx[n] = Mn[n] = a[n].y;
	for(int i = n - 1; i >= 1; i --)
		Mx[i] = max(Mx[i + 1], a[i].y), 
		Mn[i] = min(Mn[i + 1], a[i].y);
	int L = 0, R = Mx[1] - Mn[1];
	while(L < R) {
		int mid = (L + R + 1) >> 1;
		if(Chck(mid)) L = mid; else R = mid - 1;
	}
	printf("%d\n", L);
	return 0;
}

\(G\)

给定长度为 \(N\) 的序列,每个点有颜色 \(c_i\),对于每个 \(1\leq K\leq N\),求随机选出 \(K\) 个点的期望不同颜色个数。

根据期望的线性性质,可以将问题变为每种颜色选或不选,形式化一点:

设 \(X(i)\) 表示是否出现了颜色 \(i\),出现了为 \(1\) 否则为 \(0\)。

\[Ans=E(\sum\limits_{i=1}^c X(i))=\sum\limits_{i=1}^cE(X(i)) \]

每个 \(E(X(i))\) 相当于颜色 \(i\) 在随机选的 \(K\) 个点中至少出现一次的概率,设颜色 \(i\) 的出现次数为 \(n_i\),那么:

\[E(X(i))=(\binom{N}{K}-\binom{N-n_i}{K})/\binom{N}{K} \]

那么每次的就有:

\[Ans=\frac{\sum\limits_{i=1}^c (\binom{N}{K}-\binom{N-n_i}{K})}{\binom{N}{K}} \]

对于每个 \(K\),都需要 \(O(c)\) 的时间计算,时间复杂度最劣为 \(O(n^2)\)。

继续分析性质,不难发现 \(\sum\) 里的值在 \(K\) 确定的情况下,只和 \(n_i\) 的值有关。

那么考虑将 \(n_i\) 相等的颜色缩为同种颜色,同时计算,设最后剩下 \(C\) 种颜色,第 \(i\) 种颜色都由原本的 \(a_i\) 种缩成。

那么有:

\[Ans=\frac{\sum\limits_{i=1}^C a_i(\binom{N}{K}-\binom{N-n_i}{K})}{\binom{N}{K}} \]

这个看上去没啥用,但是严谨分析一下 \(C\) 的数量级别。

对于 \(n_i>\sqrt N\) 的颜色,显然至多有 \(\sqrt N\) 种,否则总个数就 \(>N\) 了,显然不可能。

对于 \(n_i\leq \sqrt N\) 的颜色,至多也就 \(0\sim \sqrt N\) 这么多种。

故总个数不可能超过 \(2\sqrt N\),而且远小于这个上界,故总时间复杂度 \(O(N\sqrt N)\)。

小插曲:有人在 AtCoder 上发布了 \(O(n\log n)\) 的 Poly 做法,把标算爆了,但是看不懂。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;

#define X first
#define Y second
#define MP make_pair
typedef pair<int, int> PII;

typedef long long LL;
const int N = 5e4 + 10;
const LL MOD = 998244353;
int n, t, a[N], b[N], sum[N], num[N];
LL fac[N], inv[N];
vector<PII> v;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

LL Pow(LL a, LL b){
	LL sum = 1;
	for(; b; b >>= 1){
		if(b & 1) sum = sum * a % MOD;
		a = a * a % MOD;
	}
	return sum;
}

void Get_Inv(){
	int t = N - 10;
	fac[0] = 1;
	for(int i = 1; i <= t; i ++) fac[i] = fac[i - 1] * i % MOD;
	inv[t] = Pow(fac[t], MOD - 2);
	for(int i = t - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % MOD;
}

LL C(int n, int m){
	if(n < m) return 0;
	if(m == 0 || n == m) return 1; 
	return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int main() {
	Get_Inv(); n = read();
	for(int i = 1; i <= n; i ++) a[i] = b[i] = read();
	sort(b + 1, b + n + 1);
	t = unique(b + 1, b + n + 1) - (b + 1);
	for(int i = 1; i <= n; i ++) {
		a[i] = lower_bound(b + 1, b + t + 1, a[i]) - b;
		sum[a[i]] ++;
	}
	for(int i = 1; i <= t; i ++) num[sum[i]] ++;
	for(int i = 1; i <= n; i ++) 
		if(num[i]) v.push_back(MP(i, num[i]));
	for(int k = 1; k <= n; k ++) {
		LL ans = 0;
		for(int i = 0; i < (int) v.size(); i ++)
			ans = (ans + v[i].Y * (C(n, k) + MOD - C(n - v[i].X, k)) % MOD) % MOD;
		ans = ans * Pow(C(n, k), MOD - 2) % MOD;
		printf("%lld\n", ans);
	}
	return 0;
}

标签:AtCoder,215,return,Beginner,int,sum,binom,include,MOD
来源: https://www.cnblogs.com/lpf-666/p/15182709.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有