返回顶部

AtCoder Beginner Contest 215

C - One More aab aba baa

求给定字符串的字典序第 \(k\) 小的排列。

排列方式最多 \(8!\) 种,排序之后 \(next\_permutation\) 即可

Sample Code (C++)
int main()
{
	IOS; string s; int k; cin >> s >> k;
	sort(s.begin(), s.end());
	k --;
	while(k -- ) next_permutation(s.begin(), s.end());
	cout << s << endl;
	return 0;
} 

D - Coprime 2

给定一个序列 \(A\),求出 \([1, m]\) 中所有符合条件的 \(k\)

  • \(\forall i, gcd(A_i, k) = 1\)

转化成 \(k\) 与所有 \(A_i\) 没有相同的因子。
对所有 \(A_i\) 质因子分解,求出 \(A\) 序列的质因子集合, 再对每一个 \(k\) 质因子分解,若与 \(A\) 的质因子集合无交集,则合法。

Sample Code (C++)
map<int, int> mp;

bool check(int x)
{
	for(int j = 2; j * j <= x; ++ j)
		if(x % j == 0)
		{
			if(mp[j]) return 0;
			while(x % j == 0) x /= j;
		}
	if(x > 1 && mp[x]) return 0;
	return 1;
}

int main()
{
	IOS; int n, m; cin >> n >> m;
	for(int i = 1; i <= n; ++ i)
	{
		int x; cin >> x;
		for(int j = 2; j * j <= x; ++ j)
			if(x % j == 0)
			{
				while(x % j == 0) x /= j;
				mp[j] = 1; 
			}
		if(x > 1) mp[x] = 1;
	}
	vector<int> v;
	v.push_back(1);
	for(int i = 2; i <= m; ++ i)
		if(check(i)) v.push_back(i);
	cout << v.size() << endl;
	for(auto x : v) cout << x << endl;
	return 0;
} 

E - Chain Contestant

给定一个由 \(A - J\) 组成的串,求从中选出子序列满足相同的字符必须相临的方案数。

如果直接 \(dp\) , 无法得知前面是否已经出现过某种颜色。发现字符种类仅有 \(10\) 种,于是可以状态压缩记录某种颜色是否出现过。
\(dp[i][j][k]\) 为从前 \(i\) 个字符中选择,颜色状态为 \(j\), 最后选择的颜色为 \(k\) 的合法方案数。
初始状态为 \(dp[i][1 << col_i][col_i] = 1\)
\(dp[i][j][k] = dp[i - 1][j][k] + dp[i - 1][j - (1 << k)][t]\;\;(选)\)
\(dp[i][j][k] = dp[i - 1][j][k]\;\;(不选)\)
答案即为 \(\sum\limits_{i = 0}^{1023}\sum\limits_{j = 0}^9dp[n][i][j]\)。 复杂度 \(O(2^{10} * n * 10)\)

Sample Code (C++)
int n;
char str[N];
LL dp[N][1 << M][M];

int main()
{
	cin >> n >> (str + 1);
	for(int i = 1; i <= n; ++ i)
	{
		int col = str[i] - 'A';
		dp[i][1 << col][col] = 1;
		for(int j = 0; j < (1 << 10); ++ j)
		{
			for(int k = 0; k < 10; ++ k)
				if(j >> k & 1) 
				{
					dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k]) % P;	
					if(col == k) dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k]) % P;
				}
			if(j >> col & 1)
				for(int t = 0; t < 10; ++ t)	
					if((j - (1 << col)) >> t & 1)
						dp[i][j][col] = (dp[i][j][col] + dp[i - 1][j - (1 << col)][t]) % P;
		}
	}
	LL res = 0;
	for(int i = 0; i < (1 << 10); ++ i)
		for(int j = 0; j < 10; ++ j)
			res = (res + dp[n][i][j]) % P;
	cout << res << endl;
	return 0;
} 

F - Dist Max 2

给一组点,定义两点之间的距离为 \(min(|x_i - x_j|, |y_i - y_j|)\), 求两个不同点的最大距离。

最小值最大化问题,考虑二分答案。
若一个答案 \(k\) 合法,则必须满足存在两个点使得 \(|x_i - x_j| \ge k\), 并且 \(|y_i - y_j| \ge k\), 于是可以考虑一个类似于滑动窗口的做法,将点按照 \(x\) 排序,对于当前点 \(P\) 来说,若队列中的点 \(Q\)\(P\)\(x\) 坐标差 \(\ge k\) , 则 \(Q\) 可以加入答案集合,维护答案集合的最大值和最小值,判断当前点是否可以从答案集合中找出一个合法点,即 \(y_{max} - y_p \ge k\)\(y_p - y_{min} \ge k\)
复杂度 \(O(nlogn + nlogx_{max})\)

Sample Code (C++)
vector<PII> v;

bool check(int mid)
{
	queue<PII> q; int Max = 0, Min = INF;
	for(auto p : v)
	{
		while(!q.empty() && p.fi - q.front().fi >= mid) 
		{
			Max = max(Max, q.front().se);
			Min = min(Min, q.front().se);
			q.pop();
		}
		if(Max - p.se >= mid || p.se - Min >= mid) return 1;
		q.push(p);
	}
	return 0;
}

int main()
{
	IOS; int n; cin >> n;
	for(int i = 0; i < n; ++ i) { int x, y; cin >> x >> y; v.push_back({x, y}); }
	sort(v.begin(), v.end());
	int l = 0, r = 1e9, res;
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(check(mid)) { res = mid; l = mid + 1; }
		else r = mid - 1;
	}	
	cout << res << endl;
	return 0;
} 

G - Colorful Candies 2

给定长度为 \(n\) 的序列,每个位置有一种颜色 \(c_i\), 对于每一个 \(1 \le k \le n\), 求随机选 \(k\) 个的期望不同颜色数。

首先,考虑算贡献,将问题转化为某种颜色出现或者不出现,设 \(X(i)\)\(i\) 这种颜色是否出现,出现为 \(1\), 不出现为 \(0\)
假设共有 \(m\) 种颜色: \(ans = E(\sum\limits_{i = 1}^mX(i)) = \sum\limits_{i = 1}^mE(X(i))\)
对于颜色 \(i\) , \(E(X(i)) = 1 * P(X(i) = 1)\), 假设颜色 \(i\)\(n_i\) 个,\(E(X(i)) = \dfrac{\dbinom{n}{k} - \dbinom{n - n_i}{k}}{\dbinom{n}{k}}\)
\(ans = \sum\limits_{i = 1}^mE(X(i)) = \sum\limits_{i = 1}^m\dfrac{\dbinom{n}{k} - \dbinom{n - n_i}{k}}{\dbinom{n}{k}} = \dfrac{\sum\limits_{i = 1}^m(\dbinom{n}{k} - \dbinom{n - n_i}{k})}{\dbinom{n}{k}}\)
对于每一个 \(k\), 都要在 \(O(m)\) 的复杂度计算,整体复杂度 \(O(n^2)\)
发现对于每一个 \(k\) 来说,答案只和每个 \(n_i\) 有关,于是可以将 \(n_i\) 相同的颜色合并一下,同时计算,设合并后只有 \(M\) 种,第 \(i\) 种由 \(a_i\) 个数量为 \(n_i\) 的颜色组成
\(ans = \dfrac{\sum\limits_{i = 1}^m(\dbinom{n}{k} - \dbinom{n - n_i}{k})}{\dbinom{n}{k}} = \dfrac{\sum\limits_{i = 1}^Ma_i(\dbinom{n}{k} - \dbinom{n - n_i}{k})}{\dbinom{n}{k}}\)
分析一下这样合并后的时间复杂度:\(n_i > \sqrt n\) 的颜色, 不会超过 \(\sqrt n\) 种, 否则总数大于 \(n\)\(n_i \le \sqrt n\) 的颜色,只有 \(0\) ~ \(\sqrt n\) 这几种,总个数不超过 \(2\sqrt n\), 故复杂度在 \(O(n \sqrt n)\)

Sample Code (C++)
int n;
LL fac[N], ifac[N];
 
LL power(LL a, LL b)
{
	LL res = 1;
	for(; b; b >>= 1, a = a * a % P)
		if(b & 1) res = res * a % P;
	return res;
} 
 
void init()
{
	fac[0] = ifac[0] = 1;
 	for(int i = 1; i < N; i ++) fac[i] = fac[i - 1] * i % P;
	ifac[N - 1] = power(fac[N - 1], P - 2);
	for(int i = N - 2; i; -- i) ifac[i] = ifac[i + 1] * (i + 1) % P;
}
 
LL C(LL a, LL b)
{
	if(a < b) return 0;
	return fac[a] * ifac[b] % P * ifac[a - b] % P;
}

int main()
{
	init(); IOS; cin >> n;
	map<int, int> mp, v;
	for(int i = 1; i <= n; ++ i) { int x; cin >> x; mp[x] ++; }
	for(auto x : mp) v[x.se] ++;
	for(int k = 1; k <= n; ++ k)
	{
		LL ans = 0;
		for(auto x : v) ans = (ans + 1ll * x.se * (C(n, k) - C(n - x.fi, k) + P) % P) % P;
		ans = ans * power(C(n, k), P - 2) % P;
		cout << ans << endl;
	}
	return 0;
} 
posted @ 2021-08-26 21:56  __October  阅读(31)  评论(0编辑  收藏  举报