Codeforces Round 874 (Div. 3)(A~G)

A. Musical Puzzle

题意:

给一个字符串,每两个相邻的字符组成一个块,求有多少个不同的块。

思路:

利用set去重,输出set的大小即可

代码:

int n;
string str;
set<string> s;
 
void solve() {
	cin >> n >> str;
	str = " " + str;
	s.clear();
	for (int i = 2; i <= n; i++) {
		char s1 = str[i - 1], s2 = str[i];
		string s3 = "";
		s3 += s1;
		s3 += s2;
		s.insert(s3);
	}
	cout << s.size() << endl;
}

B. Restore the Weather

题意:

给出a,b两个数组和k,现在要重新排列b,使得|a[i] - b[i]| <= k,输出重新排列的k。

思路:

因为题目保证有解,所以只要最小的a对应最小的b,第二小对应…即可。可以将a,b排序,然后按照大小一一映射。

代码:

int n, k;
int a[maxn], b[maxn], id1[maxn], id2[maxn];
 
bool cmp(int i, int j) {
	return a[i] < a[j];
}
 
bool cmp2(int i, int j) {
	return b[i] < b[j];
}
 
void solve() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++)	cin >> a[i], id1[i] = i;
	for (int i = 1; i <= n; i++)	cin >> b[i], id2[i] = i;
	sort(id1 + 1, id1 + n + 1, cmp);
	sort(id2 + 1, id2 + n + 1, cmp2);
	for (int i = 1; i <= n; i++) {
		a[id1[i]] = b[id2[i]];
	}
	for (int i = 1; i <= n; i++)	cout << a[i] << ' ';
	cout << endl;
}

C. Vlad Building Beautiful Array

题意:

给出一个数组a,a[i]可以-=a[j],j随便取,问a数组能不能变成全部元素大于0且都是奇数或都是偶数,可以输出YES,不能输出NO。

思路:

偶数可以减奇数,变成奇数,所以只要最小的偶数大于最小的奇数即可,因为奇数只能减奇数变成偶数,所以最小的奇数无法变成偶数,所以机速变偶数是行不通的,最后特判一下全是奇数或全是偶数即可。

代码:

void solve() {
	int miod = 0x3f3f3f3f, miev = 0x3f3f3f3f;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x;
		if (x & 1)	miod = min(x, miod);
		else
		{
			miev = min(x, miev);
		}
	}
	if (miev > miod || miod == 0x3f3f3f3f || miev == 0x3f3f3f3f)	cout << "YES" << endl;
	else	cout << "NO" << endl;
}

D. Flipper

题意:

给一个排列,要求做一次操作,选择一个区间,翻转区间中的数,同时区间前面的数全部挪到后面去,区间后面的数全部挪到前面去。求操作后的最大字典序,输出最大字典序的排列,n <= 2000。

思路:

因为n <= 2000 ,所以可以暴力,因为字典序要最大,所以得找到最大的数,把它放到最前面,因此,选择的区间右端点r得固定在最大数前面,因为必须要进行一次操作,所以最大数因该从i==2开始找,找到右端点后,枚举左端点l即可。具体看代码。

代码:

int n, a[maxn];
 
string get(int x) {
	string ans;
	while (x) {
		ans += (x % 10 + '0');
		x /= 10;
	}
	//ans.reserve();
	string res;
	for (int i = ans.size() - 1; i >= 0; i--)	res += ans[i];
	return res;
}
 
string mymax(string a, string b) {
	if (a.size() > b.size())	return a;
	if (a.size() < b.size())	return b;
	string tem, temb;
	for (int i = 0, j = 0; i < a.size() && j < b.size(); i++, j ++ ) {
		tem = "";
		while (a[i] != ' ')	tem += a[i], i++;
		temb = "";
		while (b[j] != ' ')	temb += b[j], j++;
		if (tem.size() > temb.size())	return a;
		if (tem.size() < temb.size())	return b;
		if (tem > temb)	return a;
		if (tem < temb)	return b;
	}
	if (tem.size() > temb.size())	return a;
	if (tem.size() < temb.size())	return b;
	if (tem > temb)	return a;
	if (tem < temb)	return b;
	return a;
}
 
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)	cin >> a[i];
	string ans;
	int r = n + 1;
	int mx = 0;
	for (int i = 2; i <= n; i++) {
		if (a[i] > mx) {
			mx = a[i];
			r = i;
		}
	}
	if (r == n) {
		ans += get(a[n]) + " ";
		for (int i = 1; i <= n - 1; i++)	ans += get(a[i]) + " ";
	}
	if (r == 2) {
		string tem;
		for (int i = 2; i <= n; i++)	tem += get(a[i]) + " ";
		tem += get(a[1]) + " ";
		ans = mymax(ans, tem);
	}
	r--;
	for (int l = 1; l <= r; l++) {
		string tem;
		for (int i = r + 1; i <= n; i++)	tem += get(a[i]) + " ";
		for (int i = r; i >= l; i--) tem += get(a[i]) + " ";
		for (int i = 1; i < l; i++)	tem += get(a[i]) + " ";
		ans = mymax(ans, tem);
	}
	cout << ans << endl;
}

E. Round Dance

题意:

现在有n个人,每个人记得一个邻居,每个人可以找两个人跳舞,跳舞最少要两个人,每个人都要跳舞,首先他要去找他记得的邻居a[i],然后还可以再选一个人连起来。问最少和最多可以有多少组舞蹈。

思路:

首先用并查集将a[i]和i连起来,然后每个联通块都是一组舞蹈,最多数量就是联通块的数量,因为每个人可以找两个人组合,所以环是无法再拉入人的,因为每个人都有两条边,而当一个联通块里面有链,它就可以与其他的包含链的联通块组合起来。所以最小数量就是成环的联通块,如果有存在链的联通块,就再加一,因为链可以合并起来。至于找链,只要a[a[i]] == i,那么这个联通块里面就有链,因为一个环的话每个人都会和两个不同的人连边,如果存在两个人连了两条边,就一定不存在环。

代码

int n, a[maxn];
int p[maxn];
 
int find(int x) {
	if (p[x] != x)	p[x] = find(p[x]);
	return p[x];
}
 
void merge(int x, int y) {
	if (find(x) == find(y))	return;
	p[find(x)] = find(y);
}
 
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)	cin >> a[i];
	for (int i = 1; i <= n; i++)	p[i] = i;
	for (int i = 1; i <= n; i++)
		merge(i, a[i]);
	int mi = 0, mx = 0;
	int chain = 0;
	vector<int> e(n + 1, 0);
	for(int i = 1; i <= n; i ++ )
		if (a[a[i]] == i) {
			e[find(i)] = 1;
			chain = 1;
		}
	for (int i = 1; i <= n; i++) {
		if (find(i) == i) {
			if (!e[i])	mi++;
			mx++;
		}
	}
	cout << mi + chain << ' ' << mx << endl;
}

F. Ira and Flamenco

题意:

给一个数组,你要选m个数,且其中最大-最小要小于m,求方案数mod1e9+7。

思路:

先记录每个数出现的次数,再用set给数组排序去重一下。对于合法的m个数,其选择方案数就是每个数的数量乘起来。然后枚举每个合法区间即可,求区间方案数,可以用前缀和来求,具体看代码。

代码:

int n, a[maxn];
int m;
 
int qmi(int a, int b){
	int ans = 1;
	while(b){
		if(b & 1)	ans = (ans * a) % mod;
		b >>= 1;
		a = (a * a) % mod;
	}
	return ans;
}
 
void solve(){
	cin >> n >> m;
	map<int, int> mp;
	set<int> s;
	for(int i = 1; i <= n; i ++ ){
		cin >> a[i];
		mp[a[i]] ++ ;
		s.insert(a[i]);
	}
	n = s.size();
	vector<int> num(n + 1);
	num[0] = 1;
	int cnt = 0;
	vector<int> b(n + 1);
	for(auto i : s){
		cnt++;
		b[cnt] = i;
		num[cnt] = num[cnt - 1] * mp[i] % mod;
	}
	int ans = 0;
	for(int i = m; i <= n; i ++ )
		if(b[i] - b[i - m + 1] < m)
			ans = (ans + num[i] * qmi(num[i - m], mod - 2) % mod) % mod;
	cout << ans << endl;
}

G. Ksyusha and Chinchilla

题意:

给一颗树,你可以删除其中的边,问你能否通过删除边,使得这颗树变成若干个由三个点组成的块,如果可以输出删除边的编号,如果不可以输出-1.

思路:

深搜,返回的时候每遇到三个点就切一刀,如果返回的时候遇到大于3个点了,那就直接输出-1,像这种
图1

.如果最后返回到根节点,点数不够3,也输出-1,
像这种
图2

代码:

int n;
vector<pair<int, int>> g[maxn];
int d[maxn];
vector<int> ans;
int ok = 1;
 
void dfs(int u, int f, int id){
	if(!ok)	return;
	d[u] = 1;
	for(auto tem : g[u]){
		int x = tem.first, y = tem.second;
		if(x == f)	continue;
		dfs(x, u, y);
		d[u] += d[x];
	}
	if(d[u] > 3)	ok = 0;
	if(d[u] == 3){
		d[u] = 0;
		ans.push_back(id);
	}
}
 
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i ++ )	g[i].clear();
	for(int i = 1; i <= n - 1; i ++ ){
		int u, v;
		cin >> u >> v;
		g[u].push_back({v, i});
		g[v].push_back({u, i});
	}
	ok = 1;
	ans.clear();
	dfs(1, 1, 0);
	if(!ok || d[1])	cout << -1 << endl;
	else{
		ans.pop_back();
		cout << ans.size() << endl;
		for(auto i : ans)	cout << i << ' ';
		cout << endl;
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方哲Beans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值